【发布时间】:2019-02-21 09:04:49
【问题描述】:
要计算列表中的元素,可以使用collections.Counter,但如果只计算部分元素呢?
我已经设置了这个示例(请注意:numpy 只是为了方便。通常该列表将包含任意 python 对象):
num_samples = 10000000
num_unique = 1000
numbers = np.random.randint(0, num_unique, num_samples)
我想计算一个数字在此列表中出现的频率,但我只对数字
这是要击败的基线。计数器只计算所有内容,这应该会产生一些开销。
%%time
counter = Counter(numbers)
CPU times: user 1.38 s, sys: 7.49 ms, total: 1.39 s
Wall time: 1.39 s
在计算迭代的同时过滤它似乎是不可能的。但是下面的代码风格很糟糕,它遍历列表两次,而不是使用单个循环:
%%time
numbers = [number for number in numbers if number<=10]
counter = Counter(numbers)
CPU times: user 1.3 s, sys: 22.1 ms, total: 1.32 s
Wall time: 1.33 s
这种加速基本上可以忽略不计。让我们尝试一个循环:
%%time
counter = defaultdict(int)
for number in numbers:
if number > 10:
continue
counter[number]+=1
CPU times: user 1.99 s, sys: 11.5 ms, total: 2 s
Wall time: 2.01 s
好吧,我的单循环更糟糕。我假设 Counter 从基于 C 的实现中获利?
接下来我尝试将列表表达式切换为生成器表达式。原则上这应该意味着生成器只循环一次,而它被计数器消耗。虽然数字令人失望,但它基本上和香草计数器一样快:
%%time
iterator = (number for number in numbers if number <= 10)
counter = Counter(iterator)
CPU times: user 1.38 s, sys: 8.51 ms, total: 1.39 s
Wall time: 1.39 s
此时我退后一步,重新计算了几次数字。三个 Counter 版本(未过滤、列表理解、生成器表达式)在速度上几乎相等。 defaultdict 版本始终慢得多。
如何有效地计算 python 列表中的元素,同时过滤元素?
【问题讨论】:
标签: python iterator iterable python-collections