任何人都可以帮助我吗?我正试图想出一种计算方法
>>> sum_widths = sum(col.width for col in cols if not col.hide)
并且还计算此总和中的项目数,而不必在cols
上进行两次传递。
看起来难以置信但是在扫描了std-lib(内置函数,itertools,functools等)之后,我甚至找不到一个可以计算迭代中成员数量的函数。我找到了函数itertools.count
,这听起来像我想要的,但它实际上只是一个欺骗性命名的range
函数。
经过一番思考后我想出了以下内容(这很简单,除了它的迟钝之外,缺少库函数可能是可以原谅的):
>>> visable_col_count = sum(col is col for col in cols if not col.hide)
但是,使用这两个函数需要两次迭代,这只是错误的方式。
作为替代方案,以下功能可以满足我的需求:
>>> def count_and_sum(iter):
>>> count = sum = 0
>>> for item in iter:
>>> count += 1
>>> sum += item
>>> return count, sum
这个问题是它需要100倍的长度(根据timeit
)作为生成器表达形式的总和。
如果有人能想出一个能做我想做的简单的单行程,请告诉我(使用Python 3.3)。
修改1
这里有很多好主意,伙计们。感谢所有回复的人。我需要一段时间才能消化所有这些答案,但我会尝试选择一个来检查。
修改2
我在我的两个简单建议(count_and_sum
函数和2个单独的sum
函数)上重复了时间,并发现我的原始时间已经过时,可能是由于自动调度的备份过程在的背景。
我也在这里给出了大部分优秀的建议作为答案,所有建议都采用相同的模型。分析这些答案对我来说是一种教育:deque
,enumerate
和reduce
的新用途以及count
和accumulate
的第一次使用。谢谢大家!
以下是使用我正在开发的用于显示的软件的结果(来自我的慢速上网本):
┌───────────────────────────────────────────────────────┐
│ 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
函数因为那是我正在使用的。
那是什么呢?“应该有一个 - 最好只有一个 - 明显的方式去做。”
答案 0 :(得分:101)
使用复数
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
答案 1 :(得分:49)
我不知道速度,但这有点漂亮:
>>> from itertools import accumulate
>>> it = range(10)
>>> max(enumerate(accumulate(it), 1))
(10, 45)
答案 2 :(得分:29)
改编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
答案 3 :(得分:23)
以下是一些可能感兴趣的时间数据:
import timeit
setup = '''
import random, functools, itertools, collections
x = [random.randint(0, 10) for _ in range(10**5)]
def count_and_sum(it):
c, s = 0, 0
for i in it:
c += 1
s += i
return c, s
def two_pass(it):
return sum(i for i in it), sum(True for i in it)
def functional(it):
return functools.reduce(lambda pair, x: (pair[0]+1, pair[1]+x), it, [0, 0])
def accumulator(it):
return max(enumerate(itertools.accumulate(it), 1))
def complex(it):
cpx = sum(x + 1j for x in it)
return cpx.real, int(cpx.imag)
def dequed(it):
return collections.deque(enumerate(itertools.accumulate(it), 1), maxlen=1)
'''
number = 100
for stmt in ['count_and_sum(x)',
'two_pass(x)',
'functional(x)',
'accumulator(x)',
'complex(x)',
'dequed(x)']:
print('{:.4}'.format(timeit.timeit(stmt=stmt, setup=setup, number=number)))
结果:
3.404 # OP's one-pass method
3.833 # OP's two-pass method
8.405 # Timothy Shields's fold method
3.892 # DSM's accumulate-based method
4.946 # 1_CR's complex-number method
2.002 # M4rtini's deque-based modification of DSM's method
鉴于这些结果,我不确定OP如何通过一次通过方法看到100倍减速。即使数据看起来与随机整数列表完全不同,也不应该发生。
此外,M4rtini的解决方案看起来很明显。
为了澄清,这些结果在CPython 3.2.3中。有关与PyPy3的比较,请参阅James_pic's answer,其中显示了某些方法的JIT编译的一些重要收益(也在a comment by M4rtini中提到。
答案 4 :(得分:20)
作为senshin
答案的后续行动,值得注意的是,性能差异很大程度上是由于CPython实现中的怪癖导致某些方法比其他方法慢(例如,for
循环在CPython中相对较慢)。我认为在PyPy(使用PyPy3 2.1 beta)中尝试完全相同的测试会很有趣,它具有不同的性能特征。在PyPy中,结果如下:
0.6227 # OP's one-pass method
0.8714 # OP's two-pass method
1.033 # Timothy Shields's fold method
6.354 # DSM's accumulate-based method
1.287 # 1_CR's complex-number method
3.857 # M4rtini's deque-based modification of DSM's method
在这种情况下,OP的一次通过方法最快。这是有道理的,因为它可以说是最简单的(至少从编译器的角度来看),并且PyPy可以通过内联方法调用来消除许多开销,而CPython则不能。
为了比较,我机器上的CPython 3.3.2给出了以下内容:
1.651 # OP's one-pass method
1.825 # OP's two-pass method
3.258 # Timothy Shields's fold method
1.684 # DSM's accumulate-based method
3.072 # 1_CR's complex-number method
1.191 # M4rtini's deque-based modification of DSM's method
答案 5 :(得分:5)
你可以使用类似于
的技巧来计算总和>>> from itertools import count
>>> cnt = count()
>>> sum((next(cnt), x)[1] for x in range(10) if x%2)
25
>>> next(cnt)
5
但是,使用for循环可能会更具可读性
答案 6 :(得分:4)
您可以使用:
from itertools import count
lst = range(10)
c = count(1)
tot = sum(next(c) and x for x in lst if x % 2)
n = next(c)-1
print(n, tot)
# 5 25
这是一种黑客行为,但效果非常好。
答案 7 :(得分:3)
我不知道Python语法是什么,但你可能会使用折叠。像这样:
(count, total) = fold((0, 0), lambda pair, x: (pair[0] + 1, pair[1] + x))
想法是使用(0,0)的种子,然后在每个步骤中将第一个组件加1,将当前数加到第二个组件。
为了进行比较,您可以将sum
实现为折叠,如下所示:
total = fold(0, lambda t, x: t + x)
答案 8 :(得分:3)
1_CR的复数解决方案很可爱,但过于苛刻。它起作用的原因是复数是2元组,它按元素求和。 numpy数组也是如此,我认为使用它们会更加清晰:
import numpy as np
z = [1, 2, 4, 5, 6]
y = sum(np.array([x, 1]) for x in z)
sum_z, count_z = y[0], y[1]
print sum_z, count_z
18 5
答案 9 :(得分:3)
需要考虑的其他事项:如果可以确定最小可能的计数,我们可以让高效的内置sum
完成部分工作:
from itertools import islice
def count_and_sum(iterable):
# insert favorite implementation here
def count_and_sum_with_min_count(iterable, min_count):
iterator = iter(iterable)
slice_sum = sum(islice(iterator, None, min_count))
rest_count, rest_sum = count_and_sum(iterator)
return min_count + rest_count, slice_sum + rest_sum
例如,使用deque
方法,序列为10000000项,min_count
为5000000,时间结果为:
count_and_sum: 1.03
count_and_sum_with_min_count: 0.63
答案 10 :(得分:2)
这个怎么样? 它似乎有效。
from functools import reduce
class Column:
def __init__(self, width, hide):
self.width = width
self.hide = hide
lst = [Column(10, False), Column(100, False), Column(1000, True), Column(10000, False)]
print(reduce(lambda acc, col: Column(col.width + acc.width, False) if not col.hide else acc, lst, Column(0, False)).width)
答案 11 :(得分:2)
你可能只需要总和&今天算了,但明天谁知道你需要什么!
这是一个易于扩展的解决方案:
def fold_parallel(itr, **fs):
res = {
k: zero for k, (zero, f) in fs.items()
}
for x in itr:
for k, (_, f) in fs.items():
res[k] = f(res[k], x)
return res
from operator import add
print(fold_parallel([1, 2, 3],
count = (0, lambda a, b: a + 1),
sum = (0, add),
))
# {'count': 3, 'sum': 6}
答案 12 :(得分:2)
感谢所有出色的答案,但我决定使用我原来的count_and_sum
函数,如下所示:
>>> cc, cs = count_and_sum(c.width for c in cols if not c.hide)
正如我对原始问题的编辑中所解释的那样,这是最快且最易读的解决方案。