我希望实现一个计数器,当计数器的大小超过某个阈值时,该计数器会丢弃最不频繁的元素。为此,我需要删除最不频繁的元素。
在Python中最快的方法是什么?
我知道counter.most_common()[-1]
,但它会创建一个完整的列表,并且在广泛完成时似乎很慢?是否有更好的命令(或者可能是不同的数据结构)?
答案 0 :(得分:1)
您可以通过借用least_common
的实施并执行必要的更改来实施most_common
。
请参阅collections
source in Py2.7:
def most_common(self, n=None):
'''List the n most common elements and their counts from the most
common to the least. If n is None, then list all element counts.
>>> Counter('abcdeabcdabcaba').most_common(3)
[('a', 5), ('b', 4), ('c', 3)]
'''
# Emulate Bag.sortedByCount from Smalltalk
if n is None:
return sorted(self.iteritems(), key=_itemgetter(1), reverse=True)
return _heapq.nlargest(n, self.iteritems(), key=_itemgetter(1))
要更改它以便检索最不常见,我们只需要进行一些调整。
import collections
from operator import itemgetter as _itemgetter
import heapq as _heapq
class MyCounter(collections.Counter):
def least_common(self, n=None):
if n is None:
return sorted(self.iteritems(), key=_itemgetter(1), reverse=False) # was: reverse=True
return _heapq.nsmallest(n, self.iteritems(), key=_itemgetter(1)) # was _heapq.nlargest
试验:
c = MyCounter("abbcccddddeeeee")
assert c.most_common() == c.least_common()[::-1]
assert c.most_common()[-1:] == c.least_common(1)
答案 1 :(得分:0)
由于您声明的目标是将计数器中的项目移除到阈值以下,只需反转计数器(因此值将成为具有该值的键列表),然后将计数器中的键移除到阈值以下。
示例:
>>> c=Counter("aaaabccadddefeghizkdxxx")
>>> c
Counter({'a': 5, 'd': 4, 'x': 3, 'c': 2, 'e': 2, 'b': 1, 'g': 1, 'f': 1, 'i': 1, 'h': 1, 'k': 1, 'z': 1})
counts={}
for k, v in c.items():
counts.setdefault(v, []).append(k)
tol=2
for k, v in counts.items():
if k<=tol:
c=c-Counter({}.fromkeys(v, k))
>>> c
Counter({'a': 5, 'd': 4, 'x': 3})
在此示例中,删除所有小于或等于2的计数。
或者,只需重新创建计数器并与阈值进行比较:
>>> c
Counter({'a': 5, 'd': 4, 'x': 3, 'c': 2, 'e': 2, 'b': 1, 'g': 1, 'f': 1, 'i': 1, 'h': 1, 'k': 1, 'z': 1})
>>> Counter({k:v for k,v in c.items() if v>tol})
Counter({'a': 5, 'd': 4, 'x': 3})
答案 2 :(得分:0)
如果您只想获得最不常见的值,那么处理此问题的最有效方法是从计数器(字典)中获取最小值。
由于你只能说一个值是否是最低的,你实际上需要查看所有项目,因此O(n)的时间复杂度实际上是我们能得到的最低值。但是,我们不需要具有线性空间复杂度,因为我们只需要记住最低值,而不是所有值。因此,反向运行most_common()
的解决方案对我们来说太过分了。
在这种情况下,我们可以在这里使用min()
和自定义键功能:
>>> c = Counter('foobarbazbar')
>>> c
Counter({'a': 3, 'b': 3, 'o': 2, 'r': 2, 'f': 1, 'z': 1})
>>> k = min(c, key=lambda x: c[x])
>>> del c[k]
>>> c
Counter({'a': 3, 'b': 3, 'o': 2, 'r': 2, 'z': 1})
当然,由于字典是无序的,因此如果有多个具有相同的最低出现次数,则不会影响哪个最低值被删除。