使用字典统计列表中的元素数量

275
Suppose我有一个项目列表,如下所示:
['apple', 'red', 'apple', 'red', 'red', 'pear']

我想要一个可以计算列表中每个项目出现次数的字典。因此对于上面的列表,结果应该是:

{'apple': 2, 'red': 3, 'pear': 1}

在Python中,我该如何简单地做到这一点?


如果你只想计算列表中一个单独元素的实例,请参见如何计算列表项的出现次数?


2
你可以在这里获取灵感:https://dev59.com/MXE85IYBdhLWcg3wPBF8 - mykhal
https://dev59.com/vGrWa4cB1Zd3GeqP_n0T - Andrew Tonko
有人注意到输出的顺序了吗?这是否无关紧要? - Nihal Sharma
9个回答

383

在Python 2.7 和 3.1 中,有一个特殊的 Counterdict 子类)用于此目的。

>>> from collections import Counter
>>> Counter(['apple','red','apple','red','red','pear'])
Counter({'red': 3, 'apple': 2, 'pear': 1})

5
官方说辞,或者说笑话,是Guido有一台时光机.. - Muhammad Alkarouri
23
@Glenn Maynard Counter仅仅是多重集合的一种实现,而这在我看来并不是一种罕见的数据结构。事实上,在C++标准模板库中已经有了一个实现,即std::multiset(也有std::tr1::unordered_multiset),所以Guido在其重要性方面并不孤立。 - awesomo
11
@awesomo:不,这不能与std::multiset相比。std::multiset允许存储多个独特但相对相等的值,这正是使其如此有用的原因。(例如,您可以通过它们的温度比较一组位置,并使用multiset查找所有在特定温度或温度范围内的位置,同时获得set快速插入的优点。)计数器仅计算重复次数; 独特的值将丢失。这远不如std::multiset实用——它只不过是一个包装过的字典。我怀疑这是否应该被称为multiset。 - Glenn Maynard
2
@GlennMaynard 您是正确的,我忽略了std::multiset的其他(非常有用的)功能。 - awesomo
6
计数可能是一项狭窄的任务,但它是非常经常需要的。 - Radio Controlled
显示剩余4条评论

330

我喜欢:

counts = dict()
for i in items:
  counts[i] = counts.get(i, 0) + 1

.get允许你在键不存在的情况下指定默认值。


33
对于刚接触Python的人来说,这个答案在时间复杂度方面更好。 - curiousMonkey
1
这个答案适用于浮点数列表,其中一些数字可能为“0”。 - SherylHohman
5
这个答案也不需要任何额外的导入。 +1 - Hayden Holligan
1
我不明白+1部分的作用是什么。有人能解释一下吗? - Jonas Palačionis
1
@JonasPalačionis:在将值分配回该键之前,它会递增该键的计数器。也就是说,这是一个直方图,又称为频率计数。 - Peter Cordes
显示剩余3条评论

75

只需简单地使用列表属性count\

i = ['apple','red','apple','red','red','pear']
d = {x:i.count(x) for x in i}
print d

输出:

{'pear': 1, 'apple': 2, 'red': 3}

34
你使用count函数的次数等于数组中元素的个数。你的解决方案是O(n^2),而更好的简单解决方案是O(n)。请查看有关riviera's answermmdreg's answer的评论。 - Ouroborus
5
也许你可以执行 d = {x:i.count(x) for x in set(i)}。该语句用于统计 i 中每个元素出现的次数,并将结果存储在字典 d 中。 - Xenia Ioannidou
2
@XeniaIoannidou:这里的工作量是O(n * unique_elements);除非您有很多重复项,否则没有太大改善。但仍然不好;构建一个set()基本上是将元素添加到一个没有计数的哈希表中。几乎与将它们添加到具有计数并在已存在时递增计数的字典中一样费力,而这只是为了创建设置。我所描述的添加到字典的内容已经是直方图问题的完整解决方案,您就完成了,而无需花费任何时间扫描每个唯一元素的原始数组。 - Peter Cordes

63
>>> L = ['apple','red','apple','red','red','pear']
>>> from collections import defaultdict
>>> d = defaultdict(int)
>>> for i in L:
...   d[i] += 1
>>> d
defaultdict(<type 'int'>, {'pear': 1, 'apple': 2, 'red': 3})

@NickT它比itertools.Counter更混乱 - 我会感到惊讶,如果它更快... - Shadow
1
我认为@Shadow所说的itertools.Counter应该是collections.Counter - Intrastellar Explorer

32

我一直认为对于这样一个琐碎的任务,我不想要导入任何东西。但是根据collections.Counter是否更快,我可能是错误的。

items = "Whats the simpliest way to add the list items to a dictionary "

stats = {}
for i in items:
    if i in stats:
        stats[i] += 1
    else:
        stats[i] = 1

# bonus
for i in sorted(stats, key=stats.get):
    print("%d×'%s'" % (stats[i], i))

我认为这种方法可能比使用count()更好,因为它只会遍历可迭代对象一次,而count()在每次迭代时可能会搜索整个对象。我用这种方法解析了许多兆字节的统计数据,它总是相当快。


2
你的回答因其简洁而值得更多的赞誉。我曾经为此苦苦挣扎,对其他一些建议导入新库等的用户的愚蠢感到困惑。 - ntk4
2
你可以使用默认值简化它,像这样:d[key] = d.get(key, 0) + 1 - merhoo
这个答案的简洁性被低估了!有时候没有必要导入库和过度设计简单任务。 - Madhavi Jouhari

4
L = ['apple','red','apple','red','red','pear']
d = {}
[d.__setitem__(item,1+d.get(item,0)) for item in L]
print d 

提供 {'pear': 1, 'apple': 2, 'red': 3}

1
请不要滥用列表推导式来处理副作用。使用命令式循环更加清晰,而且不会创建一个无用的临时列表。 - Karl Knechtel

1
如果您使用Numpy,unique函数可以通过传递return_counts=True来告诉您每个值出现的次数:
>>> data = ['apple', 'red', 'apple', 'red', 'red', 'pear']
>>> np.unique(data, return_counts=True)
(array(['apple', 'pear', 'red'], dtype='<U5'), array([2, 1, 3]))

计数与找到的不同元素的顺序相同; 因此,我们可以使用通常的技巧来创建所需的字典(将两个元素作为单独的参数传递zip):
>>> dict(zip(*np.unique(data, return_counts=True)))
{'apple': 2, 'pear': 1, 'red': 3}

如果您有一个特别大的小整数Numpy数组输入,可以使用bincount来获得更好的性能:
>>> data = np.random.randint(10, size=100)
>>> data
array([1, 0, 0, 3, 3, 4, 2, 4, 4, 0, 4, 8, 7, 4, 4, 8, 7, 0, 0, 2, 4, 2,
       0, 9, 0, 2, 7, 0, 7, 7, 5, 6, 6, 8, 4, 2, 7, 6, 0, 3, 6, 3, 0, 4,
       8, 8, 9, 5, 2, 2, 5, 1, 1, 1, 9, 9, 5, 0, 1, 1, 9, 5, 4, 9, 5, 2,
       7, 3, 9, 0, 1, 4, 9, 1, 1, 5, 4, 7, 5, 0, 3, 5, 1, 9, 4, 8, 8, 9,
       7, 7, 7, 5, 6, 3, 2, 4, 3, 9, 6, 0])
>>> np.bincount(data)
array([14, 10,  9,  8, 14, 10,  6, 11,  7, 11])

输出数组中的第n个值表示数字n出现的次数,因此如果需要可以使用enumerate创建字典:

>>> dict(enumerate(np.bincount(data)))
{0: 14, 1: 10, 2: 9, 3: 8, 4: 14, 5: 10, 6: 6, 7: 11, 8: 7, 9: 11}

0
那是个简单的答案,伙计!
def equalizeArray(arr):
    # Counting the frequency of each element in the array
    freq = {}
    for i in arr:
        if i not in freq:
            freq[i] = 1
        else:
            freq[i] += 1
    # Finding the element with the highest frequency
    max_freq = max(freq.values())
    # Calculating the number of deletions required
    for key,value in freq.items():
        if value == max_freq:
            print(key,"been repeated:",value,"times")

你的答案可以通过提供额外的支持性信息来改进。请编辑以添加更多细节,例如引用或文档,以便其他人可以确认你的答案是否正确。你可以在帮助中心找到关于如何撰写好的答案的更多信息。 - Community

-1
mylist = [1,2,1,5,1,1,6,'a','a','b']
result = {}
for i in mylist:
    result[i] = mylist.count(i)
print(result)

1
不,这不是一个好主意。运行时复杂度为O(n^2),这基本上破坏了使用字典的初衷。与此答案存在相同的问题:https://dev59.com/K3A75IYBdhLWcg3wDUm2#36284223/ - General Grievance
“仅有代码的答案并不是高质量的答案”。虽然这段代码可能很有用,但您可以通过解释它为什么有效、如何有效、何时应该使用以及其限制是什么来改进它。请编辑您的答案,包括解释和相关文档链接。 - Stephen Ostermiller

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