如何以最快的方式从Python中删除Counter中最不频繁的元素?

时间:2016-06-03 17:22:29

标签: python data-structures

我希望实现一个计数器,当计数器的大小超过某个阈值时,该计数器会丢弃最不频繁的元素。为此,我需要删除最不频繁的元素。

在Python中最快的方法是什么?

我知道counter.most_common()[-1],但它会创建一个完整的列表,并且在广泛完成时似乎很慢?是否有更好的命令(或者可能是不同的数据结构)?

3 个答案:

答案 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})

当然,由于字典是无序的,因此如果有多个具有相同的最低出现次数,则不会影响哪个最低值被删除。