要计算列表中的元素,可以使用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列表中的元素?
答案 0 :(得分: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))
为了进行比较,我自己的代码时间比您的时间高出很多。