如何在Python中计算字典中某个值出现的次数?

38

如果我有这样的东西:

D = {'a': 97, 'c': 0 , 'b':0,'e': 94, 'r': 97 , 'g':0}
如果我想统计列表中值为“0”的出现次数,而不必迭代整个列表,这是否可能?如何实现?

2
sum(1 for value in D.values() if value == 0) - Peter Wood
5
甚至更好的写法是:sum(value == 0 for value in D.values())。该代码用于计算字典D中值为0的键值对数量。 - Mazdak
2
@PeterWood相反,布尔值确实是整数。 - Jean-François Fabre
4
布尔类型是整数的一个子类。即使不是同一种类型(数字类型也可以),1等于True: isinstance(True,int)True - Jean-François Fabre
1
在某种程度上,布尔值是整数,因此它们可以像 @k 中那样进行求和。 - Terry Jan Reedy
显示剩余3条评论
5个回答

54

此答案中所述,使用operator.countOf()是最好的方法,但你也可以在sum()函数中使用生成器,如下所示:

sum(value == 0 for value in D.values())
# Or the following which is more optimized 
sum(1 for v in D.values() if v == 0)

或者更优化的函数式方法是,你可以使用map函数并将整数的__eq__方法作为构造函数进行传递。

sum(map((0).__eq__, D.values()))

基准测试:

In [15]: D = dict(zip(range(1000), range(1000)))

In [16]: %timeit sum(map((0).__eq__, D.values()))
49.6 µs ± 770 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [17]: %timeit sum(v==0 for v in D.values())
60.9 µs ± 669 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [18]: %timeit sum(1 for v in D.values() if v == 0)
30.2 µs ± 515 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [19]: %timeit countOf(D.values(), 0)
16.8 µs ± 74.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

请注意,虽然在这种情况下使用map函数可能会更加优化,但为了对这两种方法有一个更全面和普遍的想法,您应该针对相对较大的数据集运行基准测试。然后,根据您拥有的数据结构和数量使用最适当的方法。


可能更好,因为生成器往往会慢一些。 - juanpa.arrivillaga
实际上,我发现%timeit sum([value == 0 for value in D.values()])比生成器表达式版本更快。 - juanpa.arrivillaga
@juanpa.arrivillaga 当然,有趣的是(0).__eq__不是内置函数,但在使用map时表现更佳。这意味着生成器的缺点(额外的函数调用,__next__等)比将非内置函数传递给map更具影响力。 - Mazdak
此外,实际生成器对象的创建可能比map对象的创建更耗费时间,对于这么小的一个dict,这会产生影响。 - juanpa.arrivillaga
1
sum(1 for value in D.values() if value == 0) 可能更快。但我认为 countOf 才是最好的方法。 - Kelly Bundy
显示剩余2条评论

27

或者,可以使用collections.Counter

from collections import Counter
D = {'a': 97, 'c': 0 , 'b':0,'e': 94, 'r': 97 , 'g':0}

Counter(D.values())[0]
# 3

Counter(D.values())[0] 的时间复杂度是多少? - Mohamad Ghaith Alzin

12
你可以将它转换为列表,然后进行计数,方法如下:
D = {'a': 97, 'c': 0 , 'b':0,'e': 94, 'r': 97 , 'g':0}
print(list(D.values()).count(0))
>>3

或者遍历值:

print(sum([1 for i in D.values() if i == 0]))
>>3

4
那就是使用 operator.countOf 的任务。
countOf(D.values(), 0)

使用您的示例字典进行基准测试:

1537 ns  1540 ns  1542 ns  Counter(D.values())[0]
 791 ns   800 ns   802 ns  sum(value == 0 for value in D.values())
 694 ns   697 ns   717 ns  sum(map((0).__eq__, D.values()))
 680 ns   682 ns   689 ns  sum(1 for value in D.values() if value == 0)
 599 ns   599 ns   600 ns  sum([1 for i in D.values() if i == 0])
 368 ns   369 ns   375 ns  list(D.values()).count(0)
 229 ns   231 ns   231 ns  countOf(D.values(), 0)

代码 (在线尝试):

from timeit import repeat

setup = '''
from collections import Counter
from operator import countOf
D = {'a': 97, 'c': 0 , 'b':0,'e': 94, 'r': 97 , 'g':0}
'''

E = [
    'Counter(D.values())[0]',
    'sum(value == 0 for value in D.values())',
    'sum(map((0).__eq__, D.values()))',
    'sum(1 for value in D.values() if value == 0)',
    'sum([1 for i in D.values() if i == 0])',
    'list(D.values()).count(0)',
    'countOf(D.values(), 0)',
]

for _ in range(3):
    for e in E:
        number = 10 ** 5
        ts = sorted(repeat(e, setup, number=number))[:3]
        print(*('%4d ns ' % (t / number * 1e9) for t in ts), e)
    print()

1
for i in hashmap:    
  print(Counter(hashmap.values())[hashmap[i]])

# In this way we can traverse & check the count with the help of Counter 

这已经在此答案中提供了(https://dev59.com/sVYM5IYBdhLWcg3wxiYy#48371896),因此我只能假设这是作为“感谢”或“确认”评论发布的。 - Trenton McKinney

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