【问题标题】:python counting elements in iterable with filterpython 用过滤器计算可迭代的元素
【发布时间】: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


    【解决方案1】:

    如果这是关于大型 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))
    

    ​为了比较,我自己的代码时间比你的略高。

    【讨论】:

    • 我知道唯一性,但这与 numpy 数组无关。 randint 函数非常方便设置示例。非常好的主意:)
    猜你喜欢
    • 2014-11-12
    • 2018-08-19
    • 1970-01-01
    • 2020-09-12
    • 2022-01-21
    • 1970-01-01
    • 1970-01-01
    • 2017-09-10
    • 2023-03-12
    相关资源
    最近更新 更多