从Counter中删除最不常见的元素

时间:2015-02-13 11:53:59

标签: python counter

有没有“更快的方法”从Counter中删除键值,值小于某个值?

我做了以下事情:

counter_dict = {k:v for k, v in counter_dict.items() if v > 5}

2 个答案:

答案 0 :(得分:4)

当前代码的主要问题是调用.items,它将创建所有项目的列表:

一个优化可能是使用Counter.iteritems而不是.items,以节省创建列表并再次迭代它的惩罚。

>>> from collections import Counter
>>> cnt = Counter("asbdasdbasdbadaasasdasadsa")
>>> {k:v for k,v in cnt.iteritems() if v > 5}
{'a': 10, 's': 7, 'd': 6}

另一个优化可能是不调用.items方法,而是迭代键并使用键访问值:

>>> from collections import Counter
>>> cnt = Counter("asbdasdbasdbadaasasdasadsa")
>>> {k:cnt[k] for k in cnt if cnt[k] > 5}
{'a': 10, 's': 7, 'd': 6}

如果我们尝试 衡量ipython中与%timeit 的差异,请使用带有您提到的if条件的示例计数器 { {1}}获胜

iteritems

随着条件的变化:

In [1]: import random

In [2]: from collections import Counter

In [3]: MILLION = 10**6

In [4]: cnt = Counter(random.randint(0, MILLION) for _ in xrange(MILLION))

In [5]: %timeit {k:v for k, v in cnt.iteritems() if v < 5}
10 loops, best of 3: 140 ms per loop

In [6]: %timeit {k:v for k, v in cnt.items() if v**2 < 5}
1 loops, best of 3: 290 ms per loop

In [7]: %timeit {k:cnt[k] for k in cnt if cnt[k] < 5}
1 loops, best of 3: 272 ms per loop

答案 1 :(得分:0)

所以你最好不要每次都重新创建整个字典:

to_remove = set()
for key, value in counter_dict.viewitems():
   if value <= 5:
      to_remove.add(key)

for key in to_remove:
    del counter_dict[key]

将“for”语句展开到更多行中并不一定意味着更少的性能。虽然在这种情况下可能没有太大的性能提升,但内存消耗至少应该会下降。

另一种选择是让你的“counter_dict”成为一个更聪明的对象 当计数值<= 5时,他知道不会产生它的值 - 这会使这一步“懒惰”。

事情(但不仅仅是 - 正确的事情是实现这一点 使用ABC元类 - collections.MutableMapping

class MyDict(dict):
   def __init__(*args, **kw):
       self.threshold = None
       super(MyDict,self).__init__(*args, **kw)
   def __getitem__(self, key):
       value = super(MyDict, self).__getitem__(key)
       if self.threshold is None or key > self.threshold:
          return value
       raise ItemError
   # the same for __contains__ and other interesting methods

当你应该开始过滤时,你在dict中更改了“threshold”属性对象。这或多或少过度了 - 因为你的检查仍然会在稀释的时间内完成 - 但也许在消耗对象时,你处于异步/多线程工作负载可能会使它并行 - 但是如果你需要在不同的部分使用不同的阈值代码,它可能是一件好事。