Python中的项目频率计数

69
假设我有一个单词列表,我想要找出每个单词在该列表中出现的次数。
一种明显的方法是:
words = "apple banana apple strawberry banana lemon"
uniques = set(words.split())
freqs = [(item, words.split().count(item)) for item in uniques]
print(freqs)

但是我发现这段代码不太好,因为程序需要两次遍历单词列表,一次用来构建集合,另一次用来计算出现次数。

当然,我可以编写一个函数来遍历列表并进行计数,但那就不是很Pythonic了。那么有没有更高效和Pythonic的方法呢?


1
不要重复,看起来像是O(N*N)的复杂度。 - Drakosha
您可能会对以下内容感兴趣:https://dev59.com/6mIj5IYBdhLWcg3waERe#20308657,针对性能问题。 - JDong
14个回答

150

collections模块中的Counter是专门为解决这种类型问题而设计的:

from collections import Counter
words = "apple banana apple strawberry banana lemon"
Counter(words.split())
# Counter({'apple': 2, 'banana': 2, 'strawberry': 1, 'lemon': 1})

根据 https://dev59.com/6mIj5IYBdhLWcg3waERe#20308657,这在Python3上是最快的,但在Python2上较慢。 - JDong
你知道是否有一个标志可以将这个freq_dict转换为百分比形式吗?例如,'apple':.3333(2/6) - Tommy
@Tommy total = sum(your_counter_object.values()) 然后 freq_percentage = {k: v/total for k, v in your_counter_object.items()} - user3064538

95

defaultdict来解救!

from collections import defaultdict

words = "apple banana apple strawberry banana lemon"

d = defaultdict(int)
for word in words.split():
    d[word] += 1

这个运行时间为O(n)。


3
这是一个非常老的答案。请使用“Counter”。 - user3064538

12
freqs = {}
for word in words:
    freqs[word] = freqs.get(word, 0) + 1 # fetch and increment OR initialize

我认为这个结果与Triptych的解决方案相同,但不需要导入collections库。类似于Selinap的解决方案,但在我看来更易读。与Thomas Weigel的解决方案几乎完全相同,但没有使用异常。

然而,这可能比使用collections库中的defaultdict()慢。因为值被获取、增加然后再次分配。而不是只增加。但是使用+=可能会在内部执行相同的操作。


11

标准做法:

from collections import defaultdict

words = "apple banana apple strawberry banana lemon"
words = words.split()
result = defaultdict(int)
for word in words:
    result[word] += 1

print result

简洁的分组操作代码:

from itertools import groupby

words = "apple banana apple strawberry banana lemon"
words = words.split()

result = dict((key, len(list(group))) for key, group in groupby(sorted(words)))
print result

复杂度上有区别吗?groupby使用排序吗?那么它似乎需要O(nlogn)的时间? - Daniyar
哎呀,看起来下面的Nick Presta指出了groupby方法使用了O(nlogn)的时间复杂度。 - Daniyar

7

如果您不想使用标准字典方法(通过循环递增正确的字典键),您可以尝试以下方法:

>>> from itertools import groupby
>>> myList = words.split() # ['apple', 'banana', 'apple', 'strawberry', 'banana', 'lemon']
>>> [(k, len(list(g))) for k, g in groupby(sorted(myList))]
[('apple', 2), ('banana', 2), ('lemon', 1), ('strawberry', 1)]

它的运行时间为O(n log n)。


3

没有defaultdict:

words = "apple banana apple strawberry banana lemon"
my_count = {}
for word in words.split():
    try: my_count[word] += 1
    except KeyError: my_count[word] = 1

我的测试中似乎比defaultdict慢。 - nosklo
使用空格进行分割是多余的。此外,您应该使用dict.set_default方法而不是try/except。 - Kenan Banks
2
它变得很慢是因为你使用了异常。几乎任何语言中,异常都是非常昂贵的。避免在逻辑分支中使用它们。 看看我的解决方案,这个方法几乎相同,但不使用异常:https://dev59.com/AXNA5IYBdhLWcg3wmfS5#983434 - hopla

2
user_input = list(input().split(' '))

for word in user_input:

    print('{} {}'.format(word, user_input.count(word)))

1
words = "apple banana apple strawberry banana lemon"
w=words.split()
e=list(set(w))       
word_freqs = {}
for i in e:
    word_freqs[i]=w.count(i)
print(word_freqs)   

希望这有所帮助!

0

我碰巧在做一些Spark练习,这是我的解决方案。

tokens = ['quick', 'brown', 'fox', 'jumps', 'lazy', 'dog']

print {n: float(tokens.count(n))/float(len(tokens)) for n in tokens}

**#以上的输出结果**

{'brown': 0.16666666666666666, 'lazy': 0.16666666666666666, 'jumps': 0.16666666666666666, 'fox': 0.16666666666666666, 'dog': 0.16666666666666666, 'quick': 0.16666666666666666}

0

你不能只使用 count 吗?

words = 'the quick brown fox jumps over the lazy gray dog'
words.count('z')
#output: 1

1
该问题已经使用了“count”一词,并要求寻找更好的替代方案。 - Daniyar

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