优雅的pythonic cumsum

时间:2012-02-13 10:03:22

标签: python

实现cumsum的优雅和pythonic方法是什么? 或者 - 如果已经有一种内置的方式来做,那当然会更好......

10 个答案:

答案 0 :(得分:45)

可在Numpy中找到:

>>> import numpy as np
>>> np.cumsum([1,2,3,4,5])
array([ 1,  3,  6, 10, 15])

或者从Python 3.2开始使用itertools.accumulate

>>> from itertools import accumulate
>>> list(accumulate([1,2,3,4,5]))
[ 1,  3,  6, 10, 15]

如果Numpy不是一个选项,那么生成器循环将是我能想到的最优雅的解决方案:

def cumsum(it):
    total = 0
    for x in it:
        total += x
        yield total

实施例

>>> list(cumsum([1,2,3,4,5]))
>>> [1, 3, 6, 10, 15]

答案 1 :(得分:6)

我的想法是以功能性的方式使用reduce:

from operator import iadd
reduce(lambda acc, itm: iadd(acc, [acc[-1] + itm]), [1, 2, 3, 4, 5], [0])[1:]
>>> [1, 3, 6, 10, 15]

来自运营商模块的iadd具有进行就地添加并返回目的地的独特属性。

如果[1:]副本让你感到畏缩,你也可以这样做:

from operator import iadd
reduce(lambda acc, itm: (iadd(acc[0], [acc[1] + itm]), acc[1] + itm),
       [1, 2, 3, 4, 5], ([], 0))[0]
>>> [1, 3, 6, 10, 15]

但是我发现本地第一个例子比第一个例子快得多,而且IMO生成器比像'reduce'这样的函数式编程更加pythonic:

reduce(lambda acc, itm: (iadd(acc[0], [acc[1] + itm]), acc[1] + itm), values_ten, ([], 0))[0]
Average: 6.4593828736e-06
reduce(lambda acc, itm: (iadd(acc[0], [acc[1] + itm]), acc[1] + itm), values_mil, ([], 0))[0]
Average: 0.727404361961
reduce(lambda acc, itm: iadd(acc, [acc[-1] + itm]), values_ten, [0])[1:]
Average: 5.16271911336e-06
reduce(lambda acc, itm: iadd(acc, [acc[-1] + itm]), values_mil, [0])[1:]
Average: 0.524223491301
cumsum_rking(values_ten)
Average: 1.9828751369e-06
cumsum_rking(values_mil)
Average: 0.234241141632
list(cumsum_larsmans(values_ten))
Average: 2.02786211569e-06
list(cumsum_larsmans(values_mil))
Average: 0.201473119335

这是基准脚本,YMMV:

from timeit import timeit

def bmark(prog, setup, number):
    duration = timeit(prog, setup=setup, number=number)
    print prog
    print 'Average:', duration / number

values_ten = list(xrange(10))
values_mil = list(xrange(1000000))

from operator import iadd

bmark('reduce(lambda acc, itm: (iadd(acc[0], [acc[1] + itm]), acc[1] + itm), \
values_ten, ([], 0))[0]',
      setup='from __main__ import iadd, values_ten', number=1000000)
bmark('reduce(lambda acc, itm: (iadd(acc[0], [acc[1] + itm]), acc[1] + itm), \
values_mil, ([], 0))[0]',
      setup='from __main__ import iadd, values_mil', number=10)

bmark('reduce(lambda acc, itm: iadd(acc, [acc[-1] + itm]), \
values_ten, [0])[1:]',
      setup='from __main__ import iadd, values_ten', number=1000000)
bmark('reduce(lambda acc, itm: iadd(acc, [acc[-1] + itm]), \
values_mil, [0])[1:]',
      setup='from __main__ import iadd, values_mil', number=10)

def cumsum_rking(iterable):
    values = list(iterable)
    for pos in xrange(1, len(values)):
        values[pos] += values[pos - 1]
    return values

bmark('cumsum_rking(values_ten)',
      setup='from __main__ import cumsum_rking, values_ten', number=1000000)
bmark('cumsum_rking(values_mil)',
      setup='from __main__ import cumsum_rking, values_mil', number=10)

def cumsum_larsmans(iterable):
    total = 0
    for value in iterable:
        total += value
        yield total

bmark('list(cumsum_larsmans(values_ten))',
      setup='from __main__ import cumsum_larsmans, values_ten', number=1000000)
bmark('list(cumsum_larsmans(values_mil))',
      setup='from __main__ import cumsum_larsmans, values_mil', number=10)

这是我的Python版本字符串:

Python 2.7 (r27:82525, Jul  4 2010, 09:01:59) [MSC v.1500 32 bit (Intel)] on win32

答案 2 :(得分:5)

a = [1, 2, 3 ,4, 5]

# Using list comprehention
cumsum = [sum(a[:i+1]) for i in range(len(a))]           # [1, 3, 6, 10, 15]

# Using map()
cumsum = map(lambda i:  sum(a[:i+1]), range(len(a)))     # [1, 3, 6, 10, 15]

答案 3 :(得分:2)

到位:

a=[1,2,3,4,5]
def cumsum(a):
    for i in range(1,len(a)):
        a[i]+=a[i-1]

cumsum(a)
print a
"[1, 3, 6, 10, 15]"

答案 4 :(得分:2)

def cumsum(a):
     return map(lambda x: sum( a[0:x+1] ), range( 0, len(a) ))

cumsum([1,2,3])

> [1, 3, 6]

答案 5 :(得分:1)

for循环是pythonic

def cumsum(vec):
    r = [vec[0]]
    for val in vec[1:]:
        r.append(r[-1] + val)
    return r

答案 6 :(得分:1)

a=[1,2,3,4,5]

def cumsum(a):
    a=iter(a)
    cc=[next(a)]
    for i in a:
        cc.append(cc[-1]+i)
    return cc

print cumsum(a)
"[1, 3, 6, 10, 15]"

答案 7 :(得分:1)

a=[1,2,3,4,5]
def cumsum(a):
    b=a[:]
    for i in range(1,len(a)):
        b[i]+=b[i-1]
    return b

print cumsum(a)
"[1, 3, 6, 10, 15]"

答案 8 :(得分:0)

我更新了@GrantJ的答案,并在Jupyter中使用Python 3进行了计时。我添加了以下简单的accumulate示例:

def cumsum_acc(iterable):
    return accumulate(iterable)

时间表明这种方法是最快的:

reduce(lambda acc, itm: (iadd(acc[0], [acc[1] + itm]), acc[1] + itm), values_ten, ([], 0))[0]
Average: 4.18553415873987e-06
reduce(lambda acc, itm: (iadd(acc[0], [acc[1] + itm]), acc[1] + itm), values_mil, ([], 0))[0]
Average: 0.4559302114011018
reduce(lambda acc, itm: iadd(acc, [acc[-1] + itm]), values_ten, [0])[1:]
Average: 3.3726942356950078e-06
reduce(lambda acc, itm: iadd(acc, [acc[-1] + itm]), values_mil, [0])[1:]
Average: 0.4217967894903154
cumsum_rking(values_ten)
Average: 2.2734952410773416e-06
cumsum_rking(values_mil)
Average: 0.24106813411868303
list(cumsum_larsmans(values_ten))
Average: 1.5819200433296032e-06
list(cumsum_larsmans(values_mil))
Average: 0.17061943569953542
list(cumsum_acc(values_ten))
Average: 9.456979988264607e-07
list(cumsum_acc(values_mil))
Average: 0.11057746014014924

其背后的代码(由GrantJ编写,针对Python 3进行了修改):

from timeit import timeit
from itertools import accumulate
from functools import reduce

def bmark(prog, setup, number):
    duration = timeit(prog, setup=setup, number=number)
    print(prog)
    print('Average:', duration / number)

values_ten = list(range(10))
values_mil = list(range(1000000))

from operator import iadd

bmark('reduce(lambda acc, itm: (iadd(acc[0], [acc[1] + itm]), acc[1] + itm), \
values_ten, ([], 0))[0]',
      setup='from __main__ import iadd, reduce, values_ten', number=1000000)
bmark('reduce(lambda acc, itm: (iadd(acc[0], [acc[1] + itm]), acc[1] + itm), \
values_mil, ([], 0))[0]',
      setup='from __main__ import iadd, reduce, values_mil', number=10)

bmark('reduce(lambda acc, itm: iadd(acc, [acc[-1] + itm]), \
values_ten, [0])[1:]',
      setup='from __main__ import iadd, reduce, values_ten', number=1000000)
bmark('reduce(lambda acc, itm: iadd(acc, [acc[-1] + itm]), \
values_mil, [0])[1:]',
      setup='from __main__ import iadd, reduce, values_mil', number=10)

def cumsum_rking(iterable):
    values = list(iterable)
    for pos in range(1, len(values)):
        values[pos] += values[pos - 1]
    return values

bmark('cumsum_rking(values_ten)',
      setup='from __main__ import cumsum_rking, values_ten', number=1000000)
bmark('cumsum_rking(values_mil)',
      setup='from __main__ import cumsum_rking, values_mil', number=10)

def cumsum_larsmans(iterable):
    total = 0
    for value in iterable:
        total += value
        yield total

bmark('list(cumsum_larsmans(values_ten))',
      setup='from __main__ import cumsum_larsmans, values_ten', number=1000000)
bmark('list(cumsum_larsmans(values_mil))',
      setup='from __main__ import cumsum_larsmans, values_mil', number=10)

def cumsum_acc(iterable):
    return accumulate(iterable)

bmark('list(cumsum_acc(values_ten))',
      setup='from __main__ import cumsum_acc, accumulate, values_ten', number=1000000)
bmark('list(cumsum_acc(values_mil))',
      setup='from __main__ import cumsum_acc, accumulate, values_mil', number=10)

答案 9 :(得分:0)

Python 3.8开始并引入assignment expressions (PEP 572):=运算符),我们可以在列表推导中使用和增加变量:

total = 0
[total := total + x for x in [1, 2, 3 ,4, 5]]
# [1, 3, 6, 10, 15]

此:

  • 将变量total初始化为0,这象征着累加和
  • 对于每个项目,这两个都:
    • 通过分配表达式
    • 使当前循环项(total)增加total := total + x
    • 同时将x映射到total的新值