从 Counter 中删除最不常见的元素
removing least common elements from Counter
是否有 "faster way" 从 Counter 中删除值小于特定值的键值对?
我做了以下事情:
counter_dict = {k:v for k, v in counter_dict.items() if v > 5}
当前代码的主要问题是对 .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}
如果我们尝试 测量 %timeit
在 ipython 中的差异,使用带有你提到的 if 条件 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
随着条件的变化:
In [8]: %timeit {k:v for k, v in cnt.iteritems() if v > 5}
10 loops, best of 3: 87 ms per loop
In [9]: %timeit {k:v for k, v in cnt.items() if v > 5}
1 loops, best of 3: 186 ms per loop
In [10]: %timeit {k:cnt[k] for k in cnt if cnt[k] > 5}
10 loops, best of 3: 153 ms per loop
所以你最好不要每次都重新创建整个字典:
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 时不产生它的值 - 这将使这一步 "lazy".
一些事情(但不仅仅是 - 正确的事情是实施这个
使用 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
并且在应该开始过滤时更改字典中的 "threshold" 属性对象。这或多或少有点过头了——因为你的检查仍然会完成,只是稀释了时间——但也许在消费对象时,你处于 async/multithread 可以并行进行的工作负载——但如果你需要不同的阈值代码的不同部分,拥有它可能是一件好事。
是否有 "faster way" 从 Counter 中删除值小于特定值的键值对?
我做了以下事情:
counter_dict = {k:v for k, v in counter_dict.items() if v > 5}
当前代码的主要问题是对 .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}
如果我们尝试 测量 %timeit
在 ipython 中的差异,使用带有你提到的 if 条件 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
随着条件的变化:
In [8]: %timeit {k:v for k, v in cnt.iteritems() if v > 5}
10 loops, best of 3: 87 ms per loop
In [9]: %timeit {k:v for k, v in cnt.items() if v > 5}
1 loops, best of 3: 186 ms per loop
In [10]: %timeit {k:cnt[k] for k in cnt if cnt[k] > 5}
10 loops, best of 3: 153 ms per loop
所以你最好不要每次都重新创建整个字典:
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 时不产生它的值 - 这将使这一步 "lazy".
一些事情(但不仅仅是 - 正确的事情是实施这个 使用 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
并且在应该开始过滤时更改字典中的 "threshold" 属性对象。这或多或少有点过头了——因为你的检查仍然会完成,只是稀释了时间——但也许在消费对象时,你处于 async/multithread 可以并行进行的工作负载——但如果你需要不同的阈值代码的不同部分,拥有它可能是一件好事。