如何找到列表中数字的累计和?


92
time_interval = [4, 6, 12]

我想对数字求和[4, 4+6, 4+6+12],以便得到列表t = [4, 10, 22]

我尝试了以下方法:

t1 = time_interval[0]
t2 = time_interval[1] + t1
t3 = time_interval[2] + t2
print(t1, t2, t3)  # -> 4 10 22

Answers:


128

如果您要对像这样的数组做大量的数值工作,我建议您使用numpy,它带有一个累加和函数cumsum

import numpy as np

a = [4,6,12]

np.cumsum(a)
#array([4, 10, 22])

在这种情况下,Numpy通常比纯python更快,请与@Ashwiniaccumu相比:

In [136]: timeit list(accumu(range(1000)))
10000 loops, best of 3: 161 us per loop

In [137]: timeit list(accumu(xrange(1000)))
10000 loops, best of 3: 147 us per loop

In [138]: timeit np.cumsum(np.arange(1000))
100000 loops, best of 3: 10.1 us per loop

但是,当然,如果这是您唯一使用numpy的地方,则可能不值得依赖它。


3
np.cumsun考虑到转换时间,这种情况应该以列表开头。
hpaulj

3
@hpaulj好点,对于那些从(或针对)a开始的人,list我不推荐numpy
askewchan

我不认为numpy的是最快的stackoverflow.com/questions/15889131/...
Chris_Rands

3
正如我上面提到的,同意。避免像您和@hpaulj这样的反应是为什么我试图在答案的第一行和最后一行限制其范围的原因:-/
askewchan

1
@alex:使用timeit“如果-n未给出,则通过尝试连续10次幂直到总时间至少为0.2秒来计算合适的循环数。” 如果您希望它有所作为,则可以提供-n 1000使它们全部等效的方法。
askewchan

94

在Python 2中,您可以定义自己的生成器函数,如下所示:

def accumu(lis):
    total = 0
    for x in lis:
        total += x
        yield total

In [4]: list(accumu([4,6,12]))
Out[4]: [4, 10, 22]

在Python 3.2+中,您可以使用itertools.accumulate()

In [1]: lis = [4,6,12]

In [2]: from itertools import accumulate

In [3]: list(accumulate(lis))
Out[3]: [4, 10, 22]

5
PEP 572-赋值表达式(适用于Python 3.8)显示了一种有趣的替代方法total = 0; partial_sums = [total := total + v for v in values]。我仍然希望accumulate更快。
史蒂芬·鲁姆巴尔斯基

3
@StevenRumbalski Man,我个人认为这是有史以来最糟糕的PEP。够糟糕了……
Ashwini Chaudhary

19

看哪:

a = [4, 6, 12]
reduce(lambda c, x: c + [c[-1] + x], a, [0])[1:]

将输出(按预期):

[4, 10, 22]

17
没有效率。c + [c[-1] + x]一遍又一遍地执行的总费用总计输入长度为二次的总运行时间。
user2357112支持Monica's

reduce是一次性累积总和的好方法,但是如果您要对cumsum函数进行大量调用,则生成器将有助于“预处理”您的cumulative_sum值,并在每个后续调用中在O(1)中访问它们。
斯科特·斯基尔斯

17

我对Python 3.4的前两个答案做了一个基准测试,发现itertools.accumulatenumpy.cumsum许多情况下要快,通常要快得多。但是,从注释中可以看到,情况并非总是如此,并且很难详尽地探讨所有选项。(如果您有其他感兴趣的基准测试结果,请随时添加评论或编辑此帖子。)

一些时间...

对于简短列表accumulate,速度大约快4倍:

from timeit import timeit

def sum1(l):
    from itertools import accumulate
    return list(accumulate(l))

def sum2(l):
    from numpy import cumsum
    return list(cumsum(l))

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

timeit(lambda: sum1(l), number=100000)
# 0.4243644131347537
timeit(lambda: sum2(l), number=100000)
# 1.7077815784141421

对于较长的列表accumulate,速度大约快三倍:

l = [1, 2, 3, 4, 5]*1000
timeit(lambda: sum1(l), number=100000)
# 19.174508565105498
timeit(lambda: sum2(l), number=100000)
# 61.871223849244416

如果numpy array未将转换为listaccumulate则速度仍快约2倍:

from timeit import timeit

def sum1(l):
    from itertools import accumulate
    return list(accumulate(l))

def sum2(l):
    from numpy import cumsum
    return cumsum(l)

l = [1, 2, 3, 4, 5]*1000

print(timeit(lambda: sum1(l), number=100000))
# 19.18597290944308
print(timeit(lambda: sum2(l), number=100000))
# 37.759664884768426

如果将导入内容放在两个函数之外,但仍返回a numpy arrayaccumulate则速度仍快将近2倍:

from timeit import timeit
from itertools import accumulate
from numpy import cumsum

def sum1(l):
    return list(accumulate(l))

def sum2(l):
    return cumsum(l)

l = [1, 2, 3, 4, 5]*1000

timeit(lambda: sum1(l), number=100000)
# 19.042188624851406
timeit(lambda: sum2(l), number=100000)
# 35.17324400227517

10
您不会指望飞机比火车穿越城镇快得多,特别是包括购票和安检。同样,您不会使用numpy处理list五个项目中的a ,特别是如果您不愿意接受arrayin的回报。如果所讨论的列表真的很短,那么它们的运行时间将无关紧要-依赖性和易读性必将占主导地位。但是,大量使用具有list统一长度的统一数值数据类型会很愚蠢;为此,一个numpy的array 是适当的,并且通常较快。
askewchan

@askewchan好吧,我不仅在短列表中找到了这个,而且OP的问题要求列表作为输出而不是numpy数组。也许您可以编辑您的答案,以更清楚地了解每次使用的适当时间:)
Chris_Rands

@askewchan实际上,我已经用更详细的比较编辑了答案。在任何情况下,numpy除非我忽略了某些内容,否则我会发现速度更快吗?
Chris_Rands '16

2
哦,我的,的确是:)我不会说您忽略了某些内容,但是如果不考虑您的输入和输出,则很难单独进行比较。sum2函数中的大多数时间可能是在转换l为数组。分别尝试计时a = np.array(l)np.cumsum(a)。然后尝试a = np.tile(np.arange(1, 6), 1000)VS l = [1,2,3,4,5]*1000。在执行其他数值过程(例如,首先创建或加载)的程序l中,您的工作数据可能已经在数组中,并且创建将是不变的成本。
askewchan

1
@askewchan我和您有相同的想法,因此我对a = np.array(l)进行了计时。对于没有转换为列表的sum2,并且使用numpy数组作为输入,对于长列表/数组,sum2快5倍,这要感谢我的计算机中的sum1。
Mantxu

9

尝试以下操作:累加函数与运算符add一起执行运行中的加法。

import itertools  
import operator  
result = itertools.accumulate([1,2,3,4,5], operator.add)  
list(result)

5
您无需通过,operator.add因为无论如何默认操作都是加法。
尤金·雅玛什

8

PEP 572(Python 3.8中的新增功能)的赋值表达式提供了另一种解决方案:

time_interval = [4, 6, 12]

total_time = 0
cum_time = [total_time := total_time + t for t in time_interval]

5

您可以通过一个简单的for循环以线性时间计算累积和列表:

def csum(lst):
    s = lst.copy()
    for i in range(1, len(s)):
        s[i] += s[i-1]
    return s

time_interval = [4, 6, 12]
print(csum(time_interval))  # [4, 10, 22]

标准库itertools.accumulate可能是更快的选择(因为它是用C实现的):

from itertools import accumulate
time_interval = [4, 6, 12]
print(list(accumulate(time_interval)))  # [4, 10, 22]

2
values = [4, 6, 12]
total  = 0
sums   = []

for v in values:
  total = total + v
  sums.append(total)

print 'Values: ', values
print 'Sums:   ', sums

运行此代码可以

Values: [4, 6, 12]
Sums:   [4, 10, 22]

2

在Python3中,要查找列表的累积总和,其中ith元素是原始列表中前i + 1个元素的总和,您可以执行以下操作:

a = [4 , 6 , 12]
b = []
for i in range(0,len(a)):
    b.append(sum(a[:i+1]))
print(b)

或者,您可以使用列表推导:

b = [sum(a[:x+1]) for x in range(0,len(a))]

输出量

[4,10,22]

这看起来不错,但是可以删除指向文档的链接,否则,我无法投票。
S Meaden

2

如果您想要一种不带numpy在2.7中工作的pythonic方式,这就是我的方式

l = [1,2,3,4]
_d={-1:0}
cumsum=[_d.setdefault(idx, _d[idx-1]+item) for idx,item in enumerate(l)]

现在让我们尝试一下,并针对所有其他实现进行测试

import timeit, sys
L=list(range(10000))
if sys.version_info >= (3, 0):
    reduce = functools.reduce
    xrange = range


def sum1(l):
    cumsum=[]
    total = 0
    for v in l:
        total += v
        cumsum.append(total)
    return cumsum


def sum2(l):
    import numpy as np
    return list(np.cumsum(l))

def sum3(l):
    return [sum(l[:i+1]) for i in xrange(len(l))]

def sum4(l):
    return reduce(lambda c, x: c + [c[-1] + x], l, [0])[1:]

def this_implementation(l):
    _d={-1:0}
    return [_d.setdefault(idx, _d[idx-1]+item) for idx,item in enumerate(l)]


# sanity check
sum1(L)==sum2(L)==sum3(L)==sum4(L)==this_implementation(L)
>>> True    

# PERFORMANCE TEST
timeit.timeit('sum1(L)','from __main__ import sum1,sum2,sum3,sum4,this_implementation,L', number=100)/100.
>>> 0.001018061637878418

timeit.timeit('sum2(L)','from __main__ import sum1,sum2,sum3,sum4,this_implementation,L', number=100)/100.
>>> 0.000829620361328125

timeit.timeit('sum3(L)','from __main__ import sum1,sum2,sum3,sum4,this_implementation,L', number=100)/100.
>>> 0.4606760001182556 

timeit.timeit('sum4(L)','from __main__ import sum1,sum2,sum3,sum4,this_implementation,L', number=100)/100.
>>> 0.18932826995849608

timeit.timeit('this_implementation(L)','from __main__ import sum1,sum2,sum3,sum4,this_implementation,L', number=100)/100.
>>> 0.002348129749298096

1

首先,您需要一个连续的子序列列表:

subseqs = (seq[:i] for i in range(1, len(seq)+1))

然后,您只需调用sum每个子序列:

sums = [sum(subseq) for subseq in subseqs]

(这不是最有效的方法,因为您要重复添加所有前缀。但是对于大多数用例来说,这可能无关紧要,如果您不必考虑的话,也更容易理解。运行总计。)

如果您使用的是Python 3.2或更高版本,则可以使用itertools.accumulate它来完成以下操作:

sums = itertools.accumulate(seq)

如果你正在使用3.1或更早版本,你可以复制的“等同于”源直出的文档(除了改变next(it)it.next()2.5和更早的版本)。


9
它以二次时间运行(也许对OP来说无关紧要,但值得一提)。
克里斯·泰勒

首先,当N = 3时,谁在乎二次时间?而且我不认为它过于复杂。这是两个非常简单的步骤,每个步骤将一个迭代器转换为另一个迭代器,直接翻译英语描述。(事实上​​,他使用了一种不常见的方式来定义系列,其中不包括0长度前缀,这确实使它变得更加复杂……但这是问题的内在原因,我认为最好将其放在range而不是通过做[1:]最后的事情来破解它,或者忽略它。)
abarnert 2013年

1
大概OP的实际问题不是获得部分和,[4,6,12]因为正如他在问题中所写,他已经知道那是什么!
克里斯·泰勒

@ChrisTaylor:他明确表示他已经知道如何编写此内容,但希望“一种更简单的编写方法”。
abarnert

1

试试这个:

result = []
acc = 0
for i in time_interval:
    acc += i
    result.append(acc)

1

取决于列表的长度和性能,可能会有很多答案。我可以不考虑性能就想到的一种非常简单的方法是:

a = [1, 2, 3, 4]
a = [sum(a[0:x:1]) for x in range(len(a)+1)][1:]
print(a)

[1, 3, 6, 10]

这是通过使用列表理解来完成的,这可能工作得很好,只是我在这里多次添加了子数组,您可以即兴创作并使其变得简单!

为您的努力加油!


-1
In [42]: a = [4, 6, 12]

In [43]: [sum(a[:i+1]) for i in xrange(len(a))]
Out[43]: [4, 10, 22]

对于小列表,这比@Ashwini的上述生成器方法快得多。

In [48]: %timeit list(accumu([4,6,12]))
  100000 loops, best of 3: 2.63 us per loop

In [49]: %timeit [sum(a[:i+1]) for i in xrange(len(a))]
  100000 loops, best of 3: 2.46 us per loop

对于较大的列表,肯定会使用生成器。。。

In [50]: a = range(1000)

In [51]: %timeit [sum(a[:i+1]) for i in xrange(len(a))]
  100 loops, best of 3: 6.04 ms per loop

In [52]: %timeit list(accumu(a))
  10000 loops, best of 3: 162 us per loop

1
您只选择3个项目,尝试10 ^ 4个项目。
Ashwini Chaudhary 2013年

1
没错,对于更大的列表,生成器要快得多!
reptilicus

-1

有点hacky,但似乎可以工作:

def cumulative_sum(l):
  y = [0]
  def inc(n):
    y[0] += n
    return y[0]
  return [inc(x) for x in l]

我确实认为内部函数可以y在外部词法范围内修改声明的内容,但这没有用,所以我们改用结构修改来进行一些讨厌的修改。使用生成器可能更优雅。


-1

无需使用Numpy,您可以直接在数组上循环并一路累加总和。例如:

a=range(10)
i=1
while((i>0) & (i<10)):
    a[i]=a[i-1]+a[i]
    i=i+1
print a

结果是:

[0, 1, 3, 6, 10, 15, 21, 28, 36, 45]

-1

一个纯python oneliner用于累积和:

cumsum = lambda X: X[:1] + cumsum([X[0]+X[1]] + X[2:]) if X[1:] else X

这是一个受递归累积和启发的递归版本。一些解释:

  1. 第一项X[:1]是包含上一个元素的列表,几乎与之相同[X[0]](它将抱怨为空列表)。
  2. cumsum第二项中的递归调用处理当前元素[1]和剩余列表,其长度将减少一。
  3. if X[1:]比短if len(X)>1

测试:

cumsum([4,6,12])
#[4, 10, 22]

cumsum([])
#[]

与累积产品相似:

cumprod = lambda X: X[:1] + cumprod([X[0]*X[1]] + X[2:]) if X[1:] else X

测试:

cumprod([4,6,12])
#[4, 24, 288]

-1
l = [1,-1,3]
cum_list = l

def sum_list(input_list):
    index = 1
    for i in input_list[1:]:
        cum_list[index] = i + input_list[index-1]
        index = index + 1 
    return cum_list

print(sum_list(l))

-1

这是另一个有趣的解决方案。这利用locals()了理解的命令,即在列表理解范围内生成的局部变量:

>>> [locals().setdefault(i, (elem + locals().get(i-1, 0))) for i, elem 
     in enumerate(time_interval)]
[4, 10, 22]

这是locals()每次迭代的外观:

>>> [[locals().setdefault(i, (elem + locals().get(i-1, 0))), locals().copy()][1] 
     for i, elem in enumerate(time_interval)]
[{'.0': <enumerate at 0x21f21f7fc80>, 'i': 0, 'elem': 4, 0: 4},
 {'.0': <enumerate at 0x21f21f7fc80>, 'i': 1, 'elem': 6, 0: 4, 1: 10},
 {'.0': <enumerate at 0x21f21f7fc80>, 'i': 2, 'elem': 12, 0: 4, 1: 10, 2: 22}]

对于小型列表,性能并不糟糕:

>>> %timeit list(accumulate([4, 6, 12]))
387 ns ± 7.53 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

>>> %timeit np.cumsum([4, 6, 12])
5.31 µs ± 67.8 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

>>> %timeit [locals().setdefault(i, (e + locals().get(i-1,0))) for i,e in enumerate(time_interval)]
1.57 µs ± 12 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

显然,对于较大的列表来说,价格下降了。

>>> l = list(range(1_000_000))
>>> %timeit list(accumulate(l))
95.1 ms ± 5.22 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

>>> %timeit np.cumsum(l)
79.3 ms ± 1.07 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

>>> %timeit np.cumsum(l).tolist()
120 ms ± 1.23 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

>>> %timeit [locals().setdefault(i, (e + locals().get(i-1, 0))) for i, e in enumerate(l)]
660 ms ± 5.14 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

即使该方法很丑陋且不实用,它肯定很有趣。


-2
lst = [4,6,12]

[sum(lst[:i+1]) for i in xrange(len(lst))]

如果您正在寻找更有效的解决方案(更大的列表?),生成器可能是一个不错的选择(或者,numpy如果您真的在乎perf,可以使用它)。

def gen(lst):
    acu = 0
    for num in lst:
        yield num + acu
        acu += num

print list(gen([4, 6, 12]))

-3

这将是Haskell风格的:

def wrand(vtlg):

    def helpf(lalt,lneu): 

        if not lalt==[]:
            return helpf(lalt[1::],[lalt[0]+lneu[0]]+lneu)
        else:
            lneu.reverse()
            return lneu[1:]        

    return helpf(vtlg,[0])
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.