我有一个项目列表。我的目标是在不使用collections.Counter 的情况下找到最常见的k个最常用的项目。
创意#1: 我浏览列表,将项目放入字典中,并附上计数。如果我再次遇到该项目,我会增加计数。反向键。 (项目,计数) - > (计数,项目清单),所以现在计数指向具有此类计数的项目。完成计数,直到找到k项。
创意#2,也许更快一点: 我创建了一个包装类“Count”,如下所示:
class Count:
def __init__(self, data):
self.data = data
self.count = 1
def inc(self):
self.count += 1
... __hash__, __eq__, etc...
因此,我浏览列表,并将Count(item)放入字典中。如果我再次遇到该项,我会在Count类中增加计数。然后我将在dictionary.keys()上使用Quickselect来获取前k项。
有更好/更快的方式吗?
答案 0 :(得分:4)
您可以使用defaultdict
或dict
来轻松完成工作。这基本上类似于你的第一个布局路径。
from collections import defaultdict
import random
data = [random.randint(1,10) for _ in xrange(100)]
d = defaultdict(int) # means default value is 0
# d = {}
for x in data: # go through list
d[x] += 1 # increment counts
# d[x] = d.get(x, 0) + 1
# sort dict items by value (count) in descending order
sorted_items = sorted(d.items(), key=lambda i: -i[1])
# sorted_items = sorted(d.items(), key=lambda i: i[1], reverse=True)
# extract the keys
sorted_keys = [k for k, v in sorted_items]
# take n best
n = 8
n_most = sorted_keys[:n]
# [9, 5, 10, 3, 6, 2, 4, 1]
按值(计数)和第一个n
键的切片对dict的项目(键值对)进行排序。
答案 1 :(得分:2)
如果按照您的建议,您正在寻找有趣或最佳的算法来解决此问题,而不是在Python中使用它的方式(肯定会使用collections.Counter),好的第一步是谷歌问题的名称加上“Knuth”。
按频率搜索“Knuth order values”导致我在1986年6月的ACM通讯中找到了"Programming Pearls" article,Knuth通过一个有文化的程序按频率顺序打印文件中最常见的k个单词。
这篇特别的文章是memorable,因为在Knuth使用自定义数据结构和聪明的算法提出了一个8页精心记录的解决方案后,Doug McIlroy用一个6命令的Unix管道来批评它做同样的事情,即
tr -cs A-Za-z '\n' | tr A-Z a-z | sort | uniq -c | sort -rn | sed ${1}q
将非字母字符音译为换行符,挤出多个换行符;将大写更改为小写;各种各样的;用一个代表和一个计数替换重复的单词;按数字顺序排序;并输出前$ {1}行。
无论如何,Knuth使用了什么算法?他为此目的发明了一个新的复杂数据结构,哈希特里。但是trie属性特别适用于字符串(它通过前缀组织它们)。对于您问题中的一般数据,使用哈希映射更为适用,并且与哈希特里结构没有太大差别。
在扫描输入并将每个项目放入哈希特里结构中,或者如果已经存在则增加其计数后,必须按频率对特里结构进行排序。 Knuth创建了一个包含200个指针的数组,其中第i个指向带有频率i的所有单词的链接列表,然后是频率高于200的所有单词的插入排序列表。有些恶作剧是对这些列表使用trie的结构,因此不需要额外的存储空间。
这是基于他的假设“几乎所有的频率都很小。” 更通用的方法是:
事实上,在1987年7月的期刊中,有a follow-up还有另一个有文化的程序做同样的事情,大卫汉森,这个在C中用织机(Knuth用Pascal写的,带WEB)。一个使用哈希映射来跟踪计数,然后将频率为c的每个项目添加到L [c]中的列表中。它通过使L与所看到的单词总数一样长,而不是在填充L之前确定max_count,而不是在哈希映射中放入计数时浪费了一点空间。
除了实现细节之外,事物的方案与你的想法相同#1:增加字典中的计数;然后按计数排序。 因此,这是一个非常好的选择,这是最佳策略。
答案 2 :(得分:0)
如果您不想使用collections.Counter
,可以使用简单的词典:
data = ['a', 'b', 'c', 'a', 'b']
counts = {}
for item in data:
counts[item] = counts.get(item, 0) + 1
print(counts)
输出
{'a': 2, 'c': 1, 'b': 2}
或者您也可以使用collections.defaultdict
,因此上述内容简化为
import collections
counts = collections.defaultdict(int)
for item in data:
counts[item] += 1
答案 3 :(得分:0)
如果你想让事情更快地解决这类问题,请使用heapq。最小堆为k最常见的单词。如果您有兴趣,请查看this url。