如何获取包含特定元素的列表数量?

9

我有一个列表的列表,看起来像这样

listOfLists = [
    ['a','b','c','d'],
    ['a','b'],
    ['a','c'],
    ['c','c','c','c']  
 ] 

我希望能够统计包含特定元素的列表数量。例如,我的输出应该是:
{'a':3,'b':2,'c':3,'d':1}

从上面可以看出,我不需要元素的总数。对于"c",尽管它的总数是5,但输出结果是3,因为它只在3个列表中出现。

我正在使用一个计数器来获取计数。下面可以看到相同的内容。

line_count_tags = []
for lists in lists_of_lists:
    s = set()
    for element in lists:
         s.add(t)
    lines_count_tags.append(list(s))

count = Counter([count for counts in lines_count_tags for count in counts])

所以,当我打印计数时,我得到
{'a':3,'c':3,'b':2,'d':1}

我想知道是否有更好的方法来实现我的目标。

7个回答

12

使用Counter并将每个列表转换为一个集合。set将从每个列表中删除任何重复项,以便您不会在同一列表中计算重复值:

>>> from collections import Counter

>>> Counter(item for lst in listOfLists for item in set(lst))
Counter({'a': 3, 'b': 2, 'c': 3, 'd': 1})

如果你喜欢函数式编程,你也可以将set-map后的listOfLists链式传递给Counter

>>> from collections import Counter
>>> from itertools import chain

>>> Counter(chain.from_iterable(map(set, listOfLists)))
Counter({'a': 3, 'b': 2, 'c': 3, 'd': 1})

这种方法与第一种方法完全等价(可能会更快),除了速度稍微有所提升之外。


9

在传递给Counter的生成器推导式中,我会将每个列表转换为一个集合再进行计数:

import collections
print(collections.Counter(y for x in listOfLists for y in set(x)))

结果:

Counter({'a': 3, 'c': 3, 'b': 2, 'd': 1})

实际上您已经做到了这一点,但是上面的代码省略了很多循环和临时列表的创建。


7

您也可以不使用Counter来完成:

result = {}
for lis in listOfLists:
    for element in set(lis):
        result[element] = result.get(element, 0) + 1
print result  # {'a': 3, 'c': 3, 'b': 2, 'd': 1}

不是最优雅的方法,但速度应该会更快。

5

在使用itertools.chain.from_iterableCounter方法上有一些风格上的不同,可能会看起来像:

Counter(chain.from_iterable(map(set, listOfLists)))

演示

>>> from itertools import chain
>>> from collections import Counter
>>> Counter(chain.from_iterable(map(set, listOfLists)))
Counter({'a': 3, 'b': 2, 'c': 3, 'd': 1})

粗略基准测试

%timeit Counter(item for lst in listOfLists for item in set(lst))
100000 loops, best of 3: 13.5 µs per loop

%timeit Counter(chain.from_iterable(map(set, listOfLists)))
100000 loops, best of 3: 12.4 µs per loop

我在使用CPython 2.7.11时,使用itertools.chain可以获得更快的执行速度(约40%!)。然而,Counter + itertools.chain的执行速度比我提出的“原始”方法慢4倍。 - zwer
1
@zwer 嗯,这取决于我们讨论的输入大小。我的解决方案有更多的开销,但如果你增加输入大小,它会更快。这就是为什么基准测试并不是非常重要的原因 :) - miradulo
确实如此,我只是对我的地方速度的明显差异感到惊讶,我不习惯itertools实际上胜过几乎任何东西 - 它们通常是较慢但易于阅读的选择:D - zwer

3
只需将其转换为set,使用itertools.chain.from_iterable进行扁平化,然后将其输入到Counter中即可。
from collections import Counter
from itertools import chain

inp = [
    ['a','b','c','d'],
    ['a','b'],
    ['a','c'],
    ['c','c','c','c']  
 ] 


print(Counter(chain.from_iterable(map(set, inp))))

2
这种方法使用集合推导式计算listOfLists中的唯一条目,然后使用字典推导式计算每个列表中的出现次数。
A = {val for s in listOfLists for val in s}
d = {i: sum( i in j for j in listOfLists) for i in A}
print(d) # {'a': 3, 'c': 3, 'b': 2, 'd': 1}

我承认这有点丑,但它是一种可能的解决方案(也是字典理解的很酷的用法)。 你也可以通过将 A 的计算直接移入字典理解中,使其成为单行代码。


不需要再将集合A转换为列表或使用列表推导式来填充集合,生成表达式更好...实际上,你也可以用集合推导式构建A - Copperfield
@Copperfield 谢谢您的建议。我已经进行了更改。 - nbryans

2

这是另一种使用循环的版本:

listOfLists = [
    ['a','b','c','d'],
    ['a','b'],
    ['a','c'],
    ['c','c','c','c']
    ]

final = {}
for lst in listOfLists:
    for letter in lst:
        if letter in final:
            final[letter] += 1
        else:
            final[letter] = 1

首先创建一个名为final的空字典。然后遍历每个列表中的每个字母。如果该字母尚不存在于final中,则创建一个新的键和值=1。否则,在该键的值上加1。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接