132
time_interval = [4, 6, 12]

I want to sum up the numbers like [4, 4+6, 4+6+12] in order to get the list t = [4, 10, 22].

I tried the following:

t1 = time_interval[0]
t2 = time_interval[1] + t1
t3 = time_interval[2] + t2
print(t1, t2, t3)  # -> 4 10 22
wjandrea
  • 28,235
  • 9
  • 60
  • 81
user2259323
  • 1,339
  • 2
  • 9
  • 3

25 Answers25

170

If you're doing much numerical work with arrays like this, I'd suggest numpy, which comes with a cumulative sum function cumsum:

import numpy as np

a = [4,6,12]

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

Numpy is often faster than pure python for this kind of thing, see in comparison to @Ashwini's accumu:

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

But of course if it's the only place you'll use numpy, it might not be worth having a dependence on it.

Community
  • 1
  • 1
askewchan
  • 45,161
  • 17
  • 118
  • 134
  • 3
    This should have a `np.cumsun` case that starts with a list, to take into account the conversion time. – hpaulj Aug 20 '16 at 14:56
  • 3
    Good point @hpaulj, for those starting from (or aiming for) a `list` I would not recommend `numpy`. – askewchan Aug 23 '16 at 19:09
  • 1
    I don't think numpy is fastest http://stackoverflow.com/questions/15889131/how-to-find-the-cumulative-sum-of-numbers-in-a-list/39534850#39534850 – Chris_Rands Sep 16 '16 at 15:18
  • 3
    Agreed, as I mentioned above. Avoiding reactions like yours and @hpaulj's is why I tried to limit its scope in the very first and last lines of my answer :-/ – askewchan Sep 16 '16 at 15:54
  • @askewchan is there any reason that the np.cumsum timing has 100k loops whereas the other two have only 10k loops? I don't think it will make a material difference, just wondering. – Alex Jun 07 '18 at 18:09
  • 1
    @alex: Using [`timeit`](https://docs.python.org/2/library/timeit.html), *"if `-n` is not given, a suitable number of loops is calculated by trying successive powers of 10 until the total time is at least 0.2 seconds."* If you expect it to make a difference, you can supply `-n 1000` to make them all equivalent. – askewchan Jun 13 '18 at 18:23
  • I would suggest `np.cumsum` for `np.array` lists and `itertools.accumulate` otherwise. This answer is still the best one, but it's outdated. – Wood Jan 17 '20 at 23:26
  • Please add run complexity is it implemented in O(n) or O(n^2)? – Noa Yehezkel Jun 16 '20 at 10:40
  • Why is this answer not on the top? There seems to be a problem with the ranking system. – JM217 Sep 03 '21 at 06:06
  • In this answer the np.cumsum version does not do the conversion back to a list. This operation may change the ranking – Akanni Apr 27 '22 at 15:19
125

In Python 2 you can define your own generator function like this:

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]

And in Python 3.2+ you can use itertools.accumulate():

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

In [2]: from itertools import accumulate

In [3]: list(accumulate(lis))
Out[3]: [4, 10, 22]
Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504
  • 8
    [PEP 572 -- Assignment Expressions](https://www.python.org/dev/peps/pep-0572/) (expected for Python 3.8) shows an interesting alternative `total = 0; partial_sums = [total := total + v for v in values]`. I would still expect `accumulate` to be faster. – Steven Rumbalski Jul 15 '18 at 16:46
  • 5
    @StevenRumbalski Man, I personally think that's the worst PEP ever. [Bad enough...](https://mail.python.org/pipermail/python-committers/2018-July/005664.html) – Ashwini Chaudhary Jul 15 '18 at 23:10
  • @AshwiniChaudhary: This example may let the PEP look worse than it is. It has some quite readable and very useful applications. For instance, `if (value := some_dict.get(key)) is not None: ...` which avoids the notorious double lookup. Or also in list comprehensions with an `if` filter like `[func_result for x in xs if satisfies_property(func_result := f(x))]` which also avoids the notorious double evaluation of the function `f(x)`. – bluenote10 Jun 25 '22 at 20:35
28

Try the itertools.accumulate() function.

import itertools  

list(itertools.accumulate([1,2,3,4,5]))
# [1, 3, 6, 10, 15]
ankostis
  • 8,579
  • 3
  • 47
  • 61
Vani S
  • 344
  • 3
  • 5
27

I did a bench-mark of the top two answers with Python 3.4 and I found itertools.accumulate is faster than numpy.cumsum under many circumstances, often much faster. However, as you can see from the comments, this may not always be the case, and it's difficult to exhaustively explore all options. (Feel free to add a comment or edit this post if you have further benchmark results of interest.)

Some timings...

For short lists accumulate is about 4 times faster:

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

For longer lists accumulate is about 3 times faster:

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

If the numpy array is not cast to list, accumulate is still about 2 times faster:

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

If you put the imports outside of the two functions and still return a numpy array, accumulate is still nearly 2 times faster:

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
Chris_Rands
  • 38,994
  • 14
  • 83
  • 119
  • 13
    You wouldn't expect an airplane to be faster than the train to travel across town, especially including ticket purchase and security screening. Likewise you wouldn't use numpy to process a `list` of five items, especially if you are unwilling to accept an `array` in return. If the list in question is really so short, then their running time would be _inconsequential_---dependencies and legibility would surely dominate. But wide usage of a `list` of uniform numerical data type of significant length would be silly; for that, a numpy `array` _would_ be appropriate, and usually faster. – askewchan Sep 16 '16 at 16:07
  • @askewchan well I don't just find this for short lists and the OP's question asks for a list as output rather than a numpy array. Perhaps you can edit your answer to be clearer on when each use is appropriate :) – Chris_Rands Sep 16 '16 at 16:23
  • @askewchan In fact I've edited my answer with a much more detailed comparison. Under no circumstances, do I find `numpy` to be faster, unless I've overlooked something? – Chris_Rands Sep 16 '16 at 18:12
  • 2
    Oh my, yes indeed :) I wouldn't say you've overlooked something, but the comparison is hard to make in isolation without considering your inputs and outputs. Most of the time in your `sum2` function is probably in converting `l` into an array. Try timing `a = np.array(l)` and `np.cumsum(a)` separately. Then try `a = np.tile(np.arange(1, 6), 1000)` vs `l = [1,2,3,4,5]*1000`. In a program conducting other numerical processes (like the creation or loading of `l` in the first place) your working data would probably be in an array already, and the creation would be a constant cost. – askewchan Sep 16 '16 at 18:39
  • 2
    @askewchan I got the same idea as you and therefore I did time the a = np.array(l). For the sum2 without the transformation to list, and with a numpy array as input, sum2 is 5 times faster thank sum1 in my computer in case of the long list/array. – Mantxu Mar 24 '17 at 15:47
  • Please add run time complexity for each implementation – Noa Yehezkel Jun 16 '20 at 10:42
20

Behold:

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

Will output (as expected):

[4, 10, 22]
wonder.mice
  • 7,227
  • 3
  • 36
  • 39
  • 19
    *Not* efficient. The total expense of performing `c + [c[-1] + x]` over and over adds up to a total runtime quadratic in the input length. – user2357112 May 22 '17 at 16:36
  • reduce is good for a one-off cumulative sum, but if you're doing a lot of calls to your cumsum function a generator will be useful to "preprocess" your cumulative_sum values and access them in O(1) for each subsequent call. – Scott Skiles Jul 01 '17 at 14:06
14

Assignment expressions from PEP 572 (new in Python 3.8) offer yet another way to solve this:

time_interval = [4, 6, 12]

total_time = 0
cum_time = [total_time := total_time + t for t in time_interval]
Steven Rumbalski
  • 44,786
  • 9
  • 89
  • 119
11

You can calculate the cumulative sum list in linear time with a simple for loop:

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]

The standard library's itertools.accumulate may be a faster alternative (since it's implemented in C):

from itertools import accumulate
time_interval = [4, 6, 12]
print(list(accumulate(time_interval)))  # [4, 10, 22]
Eugene Yarmash
  • 142,882
  • 41
  • 325
  • 378
7

Since python 3.8 it's possible to use Assignment expressions, so things like this became easier to implement

nums = list(range(1, 10))
print(f'array: {nums}')

v = 0
cumsum = [v := v + n for n in nums]
print(f'cumsum: {cumsum}')

produces

array: [1, 2, 3, 4, 5, 6, 7, 8, 9]
cumsum: [1, 3, 6, 10, 15, 21, 28, 36, 45]

The same technique can be applied to find the cum product, mean, etc.

p = 1
cumprod = [p := p * n for n in nums]
print(f'cumprod: {cumprod}')

s = 0
c = 0
cumavg = [(s := s + n) / (c := c + 1) for n in nums]
print(f'cumavg: {cumavg}')

results in

cumprod: [1, 2, 6, 24, 120, 720, 5040, 40320, 362880]
cumavg: [1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0]
Viacheslav Zhukov
  • 1,130
  • 9
  • 15
4

There could be many answers for this depending on the length of the list and the performance. One very simple way which I can think without thinking of the performance is this:

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

[1, 3, 6, 10]

This is by using list comprehension and this may work fairly well it is just that here I am adding over the subarray many times, you could possibly improvise on this and make it simple!

Cheers to your endeavor!

gnoodle
  • 149
  • 9
Aashish Chaubey
  • 589
  • 1
  • 8
  • 22
3

First, you want a running list of subsequences:

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

Then you just call sum on each subsequence:

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

(This isn't the most efficient way to do it, because you're adding all of the prefixes repeatedly. But that probably won't matter for most use cases, and it's easier to understand if you don't have to think of the running totals.)

If you're using Python 3.2 or newer, you can use itertools.accumulate to do it for you:

sums = itertools.accumulate(seq)

And if you're using 3.1 or earlier, you can just copy the "equivalent to" source straight out of the docs (except for changing next(it) to it.next() for 2.5 and earlier).

abarnert
  • 354,177
  • 51
  • 601
  • 671
  • 10
    This runs in quadratic time (maybe that doesn't matter for the OP, but worth mentioning). – Chris Taylor Apr 08 '13 at 21:24
  • First, when N=3, who cares about quadratic time? And I don't think it's overcomplicated. It's two very simple steps, each transforming one iterator into another, directly translating the English-language description. (The fact that he's using an uncommon way of defining series, where the 0-length prefix isn't counted, does make it a bit more complicated… but that's inherent in the problem, and I thought it was better to put that in the `range` than to hack around it by doing `[1:]` at the end, or to ignore it.) – abarnert Apr 08 '13 at 21:26
  • 1
    Presumably the OP's actual problem isn't to get the partial sums of `[4,6,12]` since, as he wrote in the question, he already knows what that is! – Chris Taylor Apr 08 '13 at 21:27
  • @ChrisTaylor: He explicitly said that he already knows how to write this, but wants "an easier way to write it". – abarnert Apr 08 '13 at 21:35
3

If You want a pythonic way without numpy working in 2.7 this would be my way of doing it

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

now let's try it and test it against all other implementations

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
Cobry
  • 4,348
  • 8
  • 33
  • 49
2
values = [4, 6, 12]
total  = 0
sums   = []

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

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

Running this code gives

Values: [4, 6, 12]
Sums:   [4, 10, 22]
Chris Taylor
  • 46,912
  • 15
  • 110
  • 154
1

Try this:

result = []
acc = 0
for i in time_interval:
    acc += i
    result.append(acc)
MostafaR
  • 3,547
  • 1
  • 17
  • 24
1
lst = [4, 6, 12]

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

If you are looking for a more efficient solution (bigger lists?) a generator could be a good call (or just use numpy if you really care about performance).

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

print list(gen([4, 6, 12]))
Eugene Yarmash
  • 142,882
  • 41
  • 325
  • 378
gonz
  • 5,226
  • 5
  • 39
  • 54
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))
Sugandha
  • 85
  • 7
1

In Python3, To find the cumulative sum of a list where the ith element is the sum of the first i+1 elements from the original list, you may do:

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

OR you may use list comprehension:

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

Output

[4,10,22]
Moinuddin Quadri
  • 46,825
  • 13
  • 96
  • 126
0
In [42]: a = [4, 6, 12]

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

This is slighlty faster than the generator method above by @Ashwini for small lists

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

For larger lists, the generator is the way to go for sure. . .

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
reptilicus
  • 10,290
  • 6
  • 55
  • 79
0

Somewhat hacky, but seems to work:

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

I did think that the inner function would be able to modify the y declared in the outer lexical scope, but that didn't work, so we play some nasty hacks with structure modification instead. It is probably more elegant to use a generator.

Vatine
  • 20,782
  • 4
  • 54
  • 70
0

Without having to use Numpy, you can loop directly over the array and accumulate the sum along the way. For example:

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

Results in:

[0, 1, 3, 6, 10, 15, 21, 28, 36, 45]
user3062149
  • 4,173
  • 4
  • 17
  • 26
0

A pure python oneliner for cumulative sum:

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

This is a recursive version inspired by recursive cumulative sums. Some explanations:

  1. The first term X[:1] is a list containing the previous element and is almost the same as [X[0]] (which would complain for empty lists).
  2. The recursive cumsum call in the second term processes the current element [1] and remaining list whose length will be reduced by one.
  3. if X[1:] is shorter for if len(X)>1.

Test:

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

cumsum([])
#[]

And simular for cumulative product:

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

Test:

cumprod([4,6,12])
#[4, 24, 288]
Friedrich
  • 117
  • 1
  • 1
0

Here's another fun solution. This takes advantage of the locals() dict of a comprehension, i.e. local variables generated inside the list comprehension scope:

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

Here's what the locals() looks for each iteration:

>>> [[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}]

Performance is not terrible for small lists:

>>> %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)

And obviously falls flat for larger lists.

>>> 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)

Even though the method is ugly and not practical, it sure is fun.

Sayandip Dutta
  • 15,602
  • 4
  • 23
  • 52
0

I think the below code is the easiest:

a=[1,1,2,1,2]
b=[a[0]]+[sum(a[0:i]) for i in range(2,len(a)+1)]
BijanSeif
  • 39
  • 5
0
    def cumulative_sum(list):
      l = []
        for i in range(len(list)):
         new_l = sum(list[:i+1])
         l.append(new_l)
        return l

    time_interval = [4, 6, 12]
    print(cumulative_sum(time_interval)

Maybe a more beginner-friendly solution.

theo_gd
  • 11
  • 3
0

So you need to make a list of cumulative sums. You can do it by using for loop and .append() method

time_interval = [4, 6, 12]

cumulative_sum = []
new_sum = 0
for i in time_interval:
    new_sum += i
    cumulative_sum.append(new_sum)
print(cumulative_sum)

or, using numpy module

import numpy

time_interval = [4, 6, 12]
c_sum = numpy.cumsum(time_interval)
print(c_sum.tolist())
Shounak Das
  • 350
  • 12
-3

This would be Haskell-style:

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])