1

I have a list [0, 1, 2, 3, 4, 5, 6] and I sum its parts so that:

l = [0, 1, 2, 3, 4, 5, 6] -> 21

l = [1, 2, 3, 4, 5, 6] -> 21

l = [2, 3, 4, 5, 6] -> 20

l = [3, 4, 5, 6] -> 18

l = [4, 5, 6] -> 15

l = [5, 6] -> 11

l = [6] -> 6

l = [] -> 0

So, I get the corresponding sums of the list's parts: [21, 21, 20, 18, 15, 11, 6, 0]

The code I use is:

[sum(l[i:]) for i in range(len(l) + 1)]

But, for lists with range greater than 100000 the code slows down significantly.

Any idea why and how to optimize it?

baduker
  • 19,152
  • 9
  • 33
  • 56
  • 2
    you are reusing precomputed values. I suggest you use cumulative sum – WiseDev Jun 18 '19 at 09:25
  • 1
    Each time you're calculating new sum - while the sums overlap. Count from the last element, either manually or using cumulative sum of the list.. – h4z3 Jun 18 '19 at 09:26
  • 1
    [`np.cumsum`](https://docs.scipy.org/doc/numpy/reference/generated/numpy.cumsum.html). – meowgoesthedog Jun 18 '19 at 09:27

3 Answers3

8

I would suggest itertools.accumulate for this (which i recall is faster than np.cumsum), with some list reversing to get your desired output:

>>> from itertools import accumulate
>>> lst = [0, 1, 2, 3, 4, 5, 6]
>>> list(accumulate(reversed(lst)))[::-1]
[21, 21, 20, 18, 15, 11, 6]

(you can trivially add 0 to the end if needed)

Chris_Rands
  • 38,994
  • 14
  • 83
  • 119
7

This might help to reduce calculation time for big lists :

l = [0, 1, 2, 3, 4, 5, 6]
output = list(np.cumsum(l[::-1]))[::-1]+[0]

Output :

[21, 21, 20, 18, 15, 11, 6, 0]

Here is one comparison over performance for four different methods, all of which does the same thing :

from timeit import timeit

def sum10(l):
    from itertools import accumulate
    return list(accumulate(reversed(l)))[::-1]+[0]

def sum11(l):
    from itertools import accumulate
    return list(accumulate(l[::-1]))[::-1]+[0]


def sum20(l):
    from numpy import cumsum
    return list(cumsum(l[::-1]))[::-1]+[0]

def sum21(l):
    from numpy import cumsum
    return list(cumsum(list(reversed(l))))[::-1]+[0]

l = list(range(1000000))
iter_0 = timeit(lambda: sum10(l), number=10)  #0.14102990700121154
iter_1 = timeit(lambda: sum11(l), number=10)  #0.1336850459993002
nump_0 = timeit(lambda: sum20(l), number=10)  #0.6019859320003889
nump_1 = timeit(lambda: sum21(l), number=10)  #0.3818727100006072
Arkistarvh Kltzuonstev
  • 6,824
  • 7
  • 26
  • 56
1

There is no clean way of doing it with list comprehensions as far as I know.

This code will work without any other libraries:

def cumulative_sum(a):
  total= 0
  for item in a:
    total += item
    yield total

list(cumulative_sum(listname))

From Python 3.8 on, there is a new operator that might help:

[(x, total := total + x) for x in items]
Felipe Sulser
  • 1,185
  • 8
  • 19