Python 如何在列表中查找数字的累积和?

Python 如何在列表中查找数字的累积和?,python,list,sum,accumulate,Python,List,Sum,Accumulate,我想把像[4,4+6,4+6+12]这样的数字加起来,得到列表t=[4,10,22] 我尝试了以下方法: time_interval = [4, 6, 12] 在Python 2中,您可以定义自己的生成器函数,如下所示: t1 = time_interval[0] t2 = time_interval[1] + t1 t3 = time_interval[2] + t2 print(t1, t2, t3) # -> 4 10 22 在Python 3.2+中,您可以使用: 首先,您需

我想把像
[4,4+6,4+6+12]
这样的数字加起来,得到列表
t=[4,10,22]

我尝试了以下方法:

time_interval = [4, 6, 12]

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

t1 = time_interval[0]
t2 = time_interval[1] + t1
t3 = time_interval[2] + t2
print(t1, t2, t3)  # -> 4 10 22
在Python 3.2+中,您可以使用:


首先,您需要一个正在运行的子序列列表:

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

In [2]: from itertools import accumulate

In [3]: list(accumulate(lis))
Out[3]: [4, 10, 22]
然后您只需在每个子序列上调用
sum

subseqs = (seq[:i] for i in range(1, len(seq)+1))
(这不是最有效的方法,因为您要重复添加所有前缀。但对于大多数用例来说,这可能并不重要,如果您不必考虑运行总数,那么更容易理解。)

如果您使用的是Python 3.2或更高版本,您可以使用:

sums = [sum(subseq) for subseq in subseqs]
如果您使用的是3.1或更早版本,您可以直接从文档中复制“等效于”源代码(除了将
next(it)
更改为
it.next()
以适用于2.5或更早版本)。

尝试以下操作:

sums = itertools.accumulate(seq)
运行此代码可以提供

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

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

print 'Values: ', values
print 'Sums:   ', sums
如果您正在寻找更高效的解决方案(更大的列表?),那么生成器可能是一个不错的选择(如果您真正关心性能,也可以使用
numpy

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

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

In [43]: [sum(a[:i+1]) for i in xrange(len(a))]
Out[43]: [4, 10, 22]
对于更大的列表,生成器是确定的方法

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

如果你对这样的数组做了大量的数值计算,我建议,这是一个累积和函数:

在这方面,Numpy通常比纯python快,请参见与以下内容的比较:


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

有些老套,但似乎很管用:

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

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

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

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

[0, 1, 3, 6, 10, 15, 21, 28, 36, 45]
将输出(如预期的那样):


这将是哈斯克尔风格:

[4, 10, 22]

我用Python3.4对前两个答案进行了基准测试,发现在许多情况下,
itertools.acculate
numpy.cumsum
快得多。然而,正如您从评论中看到的,情况可能并不总是如此,而且很难详尽地探讨所有选项。(如果您对进一步的基准测试结果感兴趣,请随意添加评论或编辑此帖子。)

一些时间安排

对于短列表,
累积
大约快4倍:

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])
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
l = [1, 2, 3, 4, 5]*1000
timeit(lambda: sum1(l), number=100000)
# 19.174508565105498
timeit(lambda: sum2(l), number=100000)
# 61.871223849244416
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
对于较长的列表,
累积
大约快3倍:

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])
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
l = [1, 2, 3, 4, 5]*1000
timeit(lambda: sum1(l), number=100000)
# 19.174508565105498
timeit(lambda: sum2(l), number=100000)
# 61.871223849244416
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
如果未将
numpy
数组
强制转换为
列表
累积
速度仍然快约2倍:

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])
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
l = [1, 2, 3, 4, 5]*1000
timeit(lambda: sum1(l), number=100000)
# 19.174508565105498
timeit(lambda: sum2(l), number=100000)
# 61.871223849244416
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
如果将导入放在这两个函数之外,并且仍然返回
numpy
数组
累积
仍然快近2倍:

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])
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
l = [1, 2, 3, 4, 5]*1000
timeit(lambda: sum1(l), number=100000)
# 19.174508565105498
timeit(lambda: sum2(l), number=100000)
# 61.871223849244416
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

如果你想要一个不让numpy在2.7中工作的Python方式,这将是我的方式

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
现在让我们尝试一下,并针对所有其他实现进行测试

l = [1,2,3,4]
_d={-1:0}
cumsum=[_d.setdefault(idx, _d[idx-1]+item) for idx,item in enumerate(l)]
赋值表达式(Python 3.8中新增)提供了另一种解决此问题的方法:

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

用于累计和的纯python oneliner:

time_interval = [4, 6, 12]

total_time = 0
cum_time = [total_time := total_time + t for t in time_interval]
这是一个递归版本,灵感来自。一些解释:

  • 第一个术语
    X[:1]
    是一个包含上一个元素的列表,几乎与
    [X[0]]]
    相同(它会抱怨列表为空)
  • 第二项中的递归
    cumsum
    调用处理当前元素
    [1]
    和剩余列表,其长度将减少1
  • 如果len(X)>1
    的X[1://code>较短
  • 测试:

    和累积产品的模拟值:

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

    试试这个: 累加函数与运算符add一起执行正在运行的加法

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

    您可以使用简单的
    for
    循环计算线性时间内的累积总和列表:

    import itertools  
    import operator  
    result = itertools.accumulate([1,2,3,4,5], operator.add)  
    list(result)
    
    标准库可能是更快的选择(因为它是用C实现的):


    在Python3中,查找
    i
    th元素所在列表的累积和 是原始列表中第一个i+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))
    
    或者您可以使用列表理解:

    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]
    
    [1,3,6,10]

    这是通过使用列表理解,这可能工作得相当好,只是在这里我在子数组上添加了很多次,你可能会即兴创作,使它变得简单


    为你的努力干杯

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

    a = [1, 2, 3, 4]
    a = [sum(a[0:x]) for x in range(1, len(a)+1)]
    print(a)
    
    以下是
    locals()
    为每个迭代查找的内容:

    >>> [locals().setdefault(i, (elem + locals().get(i-1, 0))) for i, elem 
         in enumerate(time_interval)]
    [4, 10, 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)
    

    尽管该方法丑陋且不实用,但它确实很有趣。

    这是在二次时间内运行的(也许这对OP不重要,但值得一提)。首先,当N=3时,谁会关心二次时间?我不认为它过于复杂。这是两个非常简单的步骤,每个步骤将一个迭代器转换为另一个迭代器,直接翻译英语描述。(事实上,他使用了一种不常见的方法来定义序列,其中0长度前缀不被计算在内,这确实让它变得有点复杂……但这是问题的内在原因,我认为最好将其放在
    范围内,而不是通过在末尾执行
    [1://code>来绕过它,或者忽略它。)大概OP的实际问题不是得到
    [4,6,12]
    的部分和,因为正如他在
    >>> [[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)