需要一种快速的方法在一次传递中对一个iterable进行计数和求和

2024-05-19 20:27:11 发布

您现在位置:Python中文网/ 问答频道 /正文

有人能帮我吗?我在想办法计算

>>> sum_widths = sum(col.width for col in cols if not col.hide)

还要计算这个总和中的项数,而不必在cols上进行两次传递。

看起来难以置信,但是在扫描std库(内置函数、itertools、functools等)之后,我甚至找不到一个可以计算iterable中成员数的函数。我找到了函数itertools.count,这听起来像我想要的,但它实际上只是一个名为range的函数。

经过一番思考,我想到了以下几点(这很简单,没有库函数是可以原谅的,除了它的迟钝之外):

>>> visable_col_count = sum(col is col for col in cols if not col.hide)

然而,使用这两个函数需要iterable的两次传递,这让我很恼火。

作为替代方案,以下函数可以满足我的要求:

>>> def count_and_sum(iter):
>>>     count = sum = 0
>>>     for item in iter:
>>>         count += 1
>>>         sum += item
>>>     return count, sum

问题在于,它所需的时间是生成器表达式形式总和的100倍(根据timeit)。

如果有人能想出一个简单的一行程序来满足我的需求,请告诉我(使用Python3.3)。

编辑1

这里有很多好主意,伙计们。感谢所有回复的人。我需要一段时间来消化所有这些答案,但我会和我会尽量选择一个来检查。

编辑2

我重复了两个不起眼的建议(count_and_sum函数和两个单独的sum函数)的计时,发现我原来的计时太慢了,可能是因为后台运行了一个自动定时备份进程。

在这里,我还将给出的大多数优秀建议作为答案进行计时,所有这些建议都使用相同的模型。分析这些答案对我来说是很有教育意义的:对dequeenumeratereduce的新用法,以及对countaccumulate的首次使用。谢谢大家!

下面是使用我正在开发的显示软件的结果(来自我的慢上网本):

┌───────────────────────────────────────────────────────┐
│                 Count and Sum Timing                  │
├──────────────────────────┬───────────┬────────────────┤
│          Method          │Time (usec)│Time (% of base)│
├──────────────────────────┼───────────┼────────────────┤
│count_and_sum (base)      │        7.2│            100%│
│Two sums                  │        7.5│            104%│
│deque enumerate accumulate│        7.3│            101%│
│max enumerate accumulate  │        7.3│            101%│
│reduce                    │        7.4│            103%│
│count sum                 │        7.3│            101%│
└──────────────────────────┴───────────┴────────────────┘

(我没有把复杂和折叠的方法视为太晦涩了,但无论如何还是要谢谢你。)

由于所有这些方法在时间上几乎没有差别,我决定使用count_and_sum函数(带有显式的for循环)作为最可读、最显式和最简单的(Python Zen)函数,而且它也是最快的!

我希望我能接受这些令人惊奇的答案中的一个是正确的,但它们都一样好,尽管或多或少有些模糊,所以我只是投票给每个人,接受我自己的答案是正确的(count_and_sum函数),因为这是我正在使用的。

关于“应该有一个——最好只有一个——显而易见的方法”是什么?


Tags: and方法函数答案inforifcount
3条回答

我不知道速度,但这有点漂亮:

>>> from itertools import accumulate
>>> it = range(10)
>>> max(enumerate(accumulate(it), 1))
(10, 45)

改编DSM的答案。使用deque(... maxlen=1)来节省内存使用。

import itertools 
from collections import deque 
deque(enumerate(itertools.accumulate(x), 1), maxlen=1)

ipython中的计时代码:

import itertools , random
from collections import deque 

def count_and_sum(iter):
     count = sum = 0
     for item in iter:
         count += 1
         sum += item
     return count, sum

X = [random.randint(0, 10) for _ in range(10**7)]
%timeit count_and_sum(X)
%timeit deque(enumerate(itertools.accumulate(X), 1), maxlen=1)
%timeit (max(enumerate(itertools.accumulate(X), 1)))

结果:现在比OP的方法快

1 loops, best of 3: 1.08 s per loop
1 loops, best of 3: 659 ms per loop
1 loops, best of 3: 1.19 s per loop

使用复数

z = [1, 2, 4, 5, 6]
y = sum(x + 1j for x in z)
sum_z, count_z = y.real, int(y.imag)
print sum_z, count_z
18.0 5

相关问题 更多 >