如何在列表中获得最多/最少频率?

时间:2016-05-31 00:15:17

标签: python algorithm

我有一个项目列表。我的目标是在不使用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项。

有更好/更快的方式吗?

4 个答案:

答案 0 :(得分:4)

您可以使用defaultdictdict来轻松完成工作。这基本上类似于你的第一个布局路径。

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的结构,因此不需要额外的存储空间。

这是基于他的假设“几乎所有的频率都很小。” 更通用的方法是:

  • 跟踪添加项目时看到的最大数量
  • 然后使用max_count而不是200:制作max_count列表的列表L,并遍历您的hash_map并将每个项目添加到L [c],其中c是其频率。

事实上,在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