'Cumulative sum with list comprehension

I have a list of integers:

x = [3, 5, 2, 7]

And I want to create a new list where the nth element is the sum of elements in x from 0 to n-1.

This would result in:

y = [0, 3, 8, 10]

How can I do this with list comprehension, without running a loop?



Solution 1:[1]

How about without a loop and without a list comprehension? Sadly only Python 3:

>>> x = [3, 5, 2, 7]
>>> from itertools import accumulate
>>> [0] + list(accumulate(x[:-1]))
[0, 3, 8, 10]

Update: Here's an O(n) list comprehension solution:

>>> s = [0]
>>> [s.append(s[0]+n) or s.pop(0) for n in x]
[0, 3, 8, 10]

But I only wanted to show that that's possible without too much effort. I think accumulate or a for loop are much better.

Solution 2:[2]

This gives what you need:

x = [3, 5, 2, 7]
y = [sum(x[0:i]) for i,value in enumerate(x)]
[0, 3, 8, 10]

Solution 3:[3]

If you are doing computation on lists, you may be better using numpy:

import numpy as np
x = [3, 5, 2, 7]

print(np.cumsum([0]+x[:-1]).tolist())
[0, 3, 8, 10]

Or if 0 is irrelevant:

import numpy as np
x = np.array([3, 5, 2, 7])

print( x[:-1].cumsum())
[ 3  8 10]

Or append if you want the 0:

import numpy as np
x = np.array([3, 5, 2, 7])
out = np.array(0)
print(np.append(out, x[:-1].cumsum()))
[0  3  8 10]

Whatever you do there is always a loop somewhere, I would be more worried about writing efficient code than short.

Using just python2 a regular for loop would be efficient:

x = [3, 5, 2, 7]

sm = 0
out = []
for ele in x:
    out.append(sm)
    sm += ele
print(out)
[0, 3, 8, 10]

Solution 4:[4]

A pure list comprehension, O(n) time and O(1) extra space (i.e., other than what the list comprehension needs to build the list) (Try it online!):

xs = [3, 5, 2, 7]
                           #  Equivalent for-loop version:
ys = [y                    #  ys = []
      for s in [0]         #  s = 0
      for x in xs          #  for x in xs:
      for y in [s]         #      y = s
      for s in [s + x]]    #      s = s + x
                           #      ys.append(y)
print(ys)

I renamed to plural xs and ys so that I can name the single values x and y. My s is the cumulative sum, and I remember it as y before updating s (because you asked for result [0, 3, 8, 10] instead of [3, 8, 10, 17]).

Note that since CPython 3.9, my for y in [s] and for s in [s + x] are optimized so that they don't actually build and iterate single-element lists but work just like y = s and s = s + x:

>>> import dis
>>> dis.dis('''ys = [y
...       for s in [0]
...       for x in xs
...       for y in [s]
...       for s in [s + x]]''')

             ...

  4          16 LOAD_FAST                1 (s)
             18 STORE_FAST               3 (y)

  5          20 LOAD_FAST                1 (s)
             22 LOAD_FAST                2 (x)
             24 BINARY_ADD
             26 STORE_FAST               1 (s)

             ...

Solution 5:[5]

x = [3, 5, 2, 7]

cumsumx=[sum(x[:i] for i in range(len(x))]

Solution 6:[6]

Here is one sweet one I just discovered:

s = 0
cumsum = [(s:=s+i) for i in my_list]

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Abraham Montes
Solution 3
Solution 4 Kelly Bundy
Solution 5 Suraj Rao
Solution 6 InvaderZim