Elegant pythonic cumsum

前端 未结 10 1105
-上瘾入骨i
-上瘾入骨i 2020-12-16 11:54

What would be an elegant and pythonic way to implement cumsum?
Alternatively - if there\'a already a built-in way to do it, that would be even better of course...

相关标签:
10条回答
  • 2020-12-16 12:38
    a=[1,2,3,4,5]
    def cumsum(a):
        b=a[:]
        for i in range(1,len(a)):
            b[i]+=b[i-1]
        return b
    
    print cumsum(a)
    "[1, 3, 6, 10, 15]"
    
    0 讨论(0)
  • 2020-12-16 12:39

    It's available in Numpy:

    >>> import numpy as np
    >>> np.cumsum([1,2,3,4,5])
    array([ 1,  3,  6, 10, 15])
    

    Or use itertools.accumulate since Python 3.2:

    >>> from itertools import accumulate
    >>> list(accumulate([1,2,3,4,5]))
    [ 1,  3,  6, 10, 15]
    

    If Numpy is not an option, a generator loop would be the most elegant solution I can think of:

    def cumsum(it):
        total = 0
        for x in it:
            total += x
            yield total
    

    Ex.

    >>> list(cumsum([1,2,3,4,5]))
    >>> [1, 3, 6, 10, 15]
    
    0 讨论(0)
  • 2020-12-16 12:39
    a = [1, 2, 3 ,4, 5]
    
    # Using list comprehention
    cumsum = [sum(a[:i+1]) for i in range(len(a))]           # [1, 3, 6, 10, 15]
    
    # Using map()
    cumsum = map(lambda i:  sum(a[:i+1]), range(len(a)))     # [1, 3, 6, 10, 15]
    
    0 讨论(0)
  • 2020-12-16 12:44

    My idea was to use reduce in a functional manner:

    from operator import iadd
    reduce(lambda acc, itm: iadd(acc, [acc[-1] + itm]), [1, 2, 3, 4, 5], [0])[1:]
    >>> [1, 3, 6, 10, 15]
    

    iadd from the operator module has the unique property of doing an in-place addition and returning the destination as a result.

    If that [1:] copy makes you cringe, you could likewise do:

    from operator import iadd
    reduce(lambda acc, itm: (iadd(acc[0], [acc[1] + itm]), acc[1] + itm),
           [1, 2, 3, 4, 5], ([], 0))[0]
    >>> [1, 3, 6, 10, 15]
    

    But I discovered that locally the first example is much faster and IMO generators are more pythonic than functional programming like 'reduce':

    reduce(lambda acc, itm: (iadd(acc[0], [acc[1] + itm]), acc[1] + itm), values_ten, ([], 0))[0]
    Average: 6.4593828736e-06
    reduce(lambda acc, itm: (iadd(acc[0], [acc[1] + itm]), acc[1] + itm), values_mil, ([], 0))[0]
    Average: 0.727404361961
    reduce(lambda acc, itm: iadd(acc, [acc[-1] + itm]), values_ten, [0])[1:]
    Average: 5.16271911336e-06
    reduce(lambda acc, itm: iadd(acc, [acc[-1] + itm]), values_mil, [0])[1:]
    Average: 0.524223491301
    cumsum_rking(values_ten)
    Average: 1.9828751369e-06
    cumsum_rking(values_mil)
    Average: 0.234241141632
    list(cumsum_larsmans(values_ten))
    Average: 2.02786211569e-06
    list(cumsum_larsmans(values_mil))
    Average: 0.201473119335
    

    Here's the benchmark script, YMMV:

    from timeit import timeit
    
    def bmark(prog, setup, number):
        duration = timeit(prog, setup=setup, number=number)
        print prog
        print 'Average:', duration / number
    
    values_ten = list(xrange(10))
    values_mil = list(xrange(1000000))
    
    from operator import iadd
    
    bmark('reduce(lambda acc, itm: (iadd(acc[0], [acc[1] + itm]), acc[1] + itm), \
    values_ten, ([], 0))[0]',
          setup='from __main__ import iadd, values_ten', number=1000000)
    bmark('reduce(lambda acc, itm: (iadd(acc[0], [acc[1] + itm]), acc[1] + itm), \
    values_mil, ([], 0))[0]',
          setup='from __main__ import iadd, values_mil', number=10)
    
    bmark('reduce(lambda acc, itm: iadd(acc, [acc[-1] + itm]), \
    values_ten, [0])[1:]',
          setup='from __main__ import iadd, values_ten', number=1000000)
    bmark('reduce(lambda acc, itm: iadd(acc, [acc[-1] + itm]), \
    values_mil, [0])[1:]',
          setup='from __main__ import iadd, values_mil', number=10)
    
    def cumsum_rking(iterable):
        values = list(iterable)
        for pos in xrange(1, len(values)):
            values[pos] += values[pos - 1]
        return values
    
    bmark('cumsum_rking(values_ten)',
          setup='from __main__ import cumsum_rking, values_ten', number=1000000)
    bmark('cumsum_rking(values_mil)',
          setup='from __main__ import cumsum_rking, values_mil', number=10)
    
    def cumsum_larsmans(iterable):
        total = 0
        for value in iterable:
            total += value
            yield total
    
    bmark('list(cumsum_larsmans(values_ten))',
          setup='from __main__ import cumsum_larsmans, values_ten', number=1000000)
    bmark('list(cumsum_larsmans(values_mil))',
          setup='from __main__ import cumsum_larsmans, values_mil', number=10)
    

    And here's my Python version string:

    Python 2.7 (r27:82525, Jul  4 2010, 09:01:59) [MSC v.1500 32 bit (Intel)] on win32
    
    0 讨论(0)
提交回复
热议问题