python计数元素可迭代过滤器

问题描述 投票:0回答:1

要计算列表中的元素,可以使用collections.Counter,但是如果只需要对某些元素进行计数呢?

我已经设置了这个例子(请注意:numpy只是为了方便。一般来说,列表将包含任意python对象):

num_samples = 10000000
num_unique = 1000
numbers = np.random.randint(0, num_unique, num_samples)

我想计算这个列表中出现的数字的频率,但我只对数字<= 10感兴趣。

这是要击败的基线。计数器只计算一切,这应该产生一些开销。

%%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
1个回答
2
投票

如果这是关于大型numpy数组,你最好利用矢量化的numpy操作。

%%time
np.unique(numbers[numbers <= 10], return_counts=True)

输出:

Wall time: 31.2 ms

(array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10]),
 array([10055, 10090,  9941, 10002,  9994,  9989, 10070,  9859, 10038,
        10028,  9965], dtype=int64))

相比之下,我自己的代码时间略高于你的代码。

© www.soinside.com 2019 - 2024. All rights reserved.