Code Review Stack Exchange is a question and answer site for peer programmer code reviews. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

It's a very simple question, but I wonder if there is a more pythonic way to add each previous elements in a list like this (for example with a list comprehension maybe) :

input : L = [4, 2, 1, 3]
output : new_L = [4, 6, 7, 10]

Which I've done this way :

def add_one_by_one(L):
    new_L = []
    for elt in L:
        if len(new_L)>0:
            new_L.append(new_L[-1]+elt)
        else:
            new_L.append(elt)
    return new_L

new_L = add_one_by_one(L)
share|improve this question
1  
Are you using Python 2, Python 3 or both? – Peilonrayz 14 hours ago
    
I'm using mostly Python 2 – Annabelle 14 hours ago
up vote 7 down vote accepted

Note that Python's official style-guide, PEP8, recommends using lower_case names for variables, so I changed all your Ls to l and all your new_L to new_l.

l = [4, 2, 1, 3]

You should keep track of the cumulative sum in a variable. This way you avoid having to test whether the new_l already has an element in it:

def add_one_by_one(l):
    new_l = []
    cumsum = 0
    for elt in l:
        cumsum += elt
        new_l.append(cumsum)
    return new_l

As an alternative, you could use a generator to avoid having to build the list inside the function (if you are just going to iterate over it later, this is the most memory-efficient way):

def add_one_by_one_gen(l):
    cumsum = 0
    for elt in l:
        cumsum += elt
        yield cumsum

new_l = list(add_one_by_one_gen(l))

# This takes basically no additional memory (only one float/int):
for x in add_one_by_one_gen(l):
    print x

(Replace print x with print(x) in Python 3.x.)

Probably the fastest way to do it would be using the numpy function cumsum:

import numpy as np

new_l = np.cumsum(l)
share|improve this answer
3  
Not everyone has, needs or wants to use NumPy. Let's not make it the JQuery to JavaScript questions of Stack Overflow. – Peilonrayz 14 hours ago
3  
@Peilonrayz I agree. This is why I proposed two non-numpy alternatives (where the generator is probably even better if the OP does not actually need the cumulative sums all at once). – Graipher 14 hours ago
2  
BTW, list(itertools.accumulate(l)) is faster than numpy.cumsum(l). I only tried when l was a Python list, as that's the use-case. – Peilonrayz 13 hours ago
    
@Peilonrayz Good to know. Makes sense that it only starts to be faster when used on bumpy arrays – Graipher 12 hours ago
3  
@Graipher No, arrays are always bumpy in Python. – wizzwizz4 6 hours ago

In Python 3 itertools gained a new function accumulate that does what you want. I'd recommend that you instead used this function. Or if you can't if you're in Python 2 to upgrade. This would single handedly change your code to:

from itertools import accumulate

new_l = accumulate(l)

If you however done this as a learning exercise, then I'd instead use iterators. I'd first change l to an iterator, via iter. Which would allow you to use next to remove the default value. After this I would then loop through the iterator and yield rather than new_list.append the new values. This can allow you to get something like:

def accumulate_sum(l):
    l = iter(l)
    try:
        total = next(l)
    except StopIteration:
        return
    yield total
    for item in l:
        total += item
        yield total

Which funnily enough is almost exactly the same to how it's done in itertools.accumulate. If you wanted to at a later date use a different function rather than addition, then you could pass that as a function, to call on each iteration.

share|improve this answer
    
Thanks, I didn't know about the iter function, very useful. – Annabelle 13 hours ago

Using Python 2

1.Ninja way:

Is fun and good for learning python, but don't go for it in production code

from operator import iadd

l = [4,2,1,3] 
reduce(lambda result, x: iadd(result, [result[-1] + x]), l, [0])[1:]

2.Explicit way

I will just copy @Grapier solution for this because I would do the same:

def add_one_by_one_gen(l):
    cumsum = 0
    for elt in l:
        cumsum += elt
        yield cumsum

Using Python 3

from itertools import accumulate

accumulate(l)
share|improve this answer
    
For the first solution, why would you want to use iadd instead of just result+[result[-1]+x]? – kalj 11 hours ago
    
@kalj there is no particular reason for that, you can use result + result[...] if you want to, which is a more obvious way to do that. – Alex 11 hours ago
    
@Alex is the reduce really that hard to read? You could always define an 'ungolfed' named function to pass to reduce if it required annotations. I don't write python daily, but summing a list says reduce to me in any HOF language...or is there some other reason you wouldn't use it in prod? – Jared Smith 9 hours ago
    
@JaredSmith I'm pretty upset by current python community, you would be surprised to see how many of python devs don't even know about the existence of reduce or any high-order functions in python. And a reason for that, in my opinion, is that most of the companies don't have any kind of coding culture so basically, people don't really care about things they write until they are working. – Alex 9 hours ago
2  
@JaredSmith This kind of abuse had led to reduce being moved to functools. Note that he isn't exactly summing a list - he traverses the list with an array, adding new elements to that array. Things like this are better done with iteration, or comprehension (well, not in this case, but in general), leaving reduce for tasks where set of homogenous elements is combined into one element of similar "type" (like summing a list, if we didn't have a sum for that). – Daerdemandt 9 hours ago

Since you asked for a Pythonic solution and I see none so far, I propose:

new_L = [sum(L[:i+1]) for i in range(len(L))]

It's certainly less efficient than an accumulator -- it's \$O(\frac{n^2}{2})\$ vs \$O(n)\$ -- but it uses a list comprehension as you suggested.

share|improve this answer
    
Welcome to the site! Indeed this oneliner (or, more precisely, lack of more efficient oneliners) demonstrates that sometimes syntax gets in your way - unless you resort to [element + this_list()[-1] for element in L], lending some magic from here. – Daerdemandt 55 mins ago

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.