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