python - lists - ¿Cómo encontrar la suma acumulada de números en una lista?
cumsum python pandas (16)
Algo hacky, pero parece funcionar:
def cumulative_sum(l):
y = [0]
def inc(n):
y[0] += n
return y[0]
return [inc(x) for x in l]
Pensé que la función interna podría modificar el y
declarado en el alcance léxico externo, pero eso no funcionó, así que jugamos algunos hacks desagradables con modificación de estructura. Probablemente sea más elegante usar un generador.
time_interval = [4, 6, 12]
Quiero resumir los números como [4, 4+6, 4+6+12]
para obtener la lista t = [4, 10, 22]
.
Intenté lo siguiente:
for i in time_interval:
t1 = time_interval[0]
t2 = time_interval[1] + t1
t3 = time_interval[2] + t2
print(t1, t2, t3)
4 10 22
4 10 22
4 10 22
En Python 2 puedes definir tu propia función de generador así:
def accumu(lis):
total = 0
for x in lis:
total += x
yield total
In [4]: list(accumu([4,6,12]))
Out[4]: [4, 10, 22]
Y en Python 3.2+ puedes usar itertools.accumulate()
:
In [1]: lis = [4,6,12]
In [2]: from itertools import accumulate
In [3]: list(accumulate(lis))
Out[3]: [4, 10, 22]
Este sería el estilo Haskell:
def wrand(vtlg):
def helpf(lalt,lneu):
if not lalt==[]:
return helpf(lalt[1::],[lalt[0]+lneu[0]]+lneu)
else:
lneu.reverse()
return lneu[1:]
return helpf(vtlg,[0])
Hice un itertools.accumulate
de las dos respuestas principales con Python 3.4 y encontré itertools.accumulate
es más rápido que numpy.cumsum
en muchas circunstancias, a menudo mucho más rápido. Sin embargo, como puede ver en los comentarios, este no siempre es el caso, y es difícil explorar exhaustivamente todas las opciones. (Siéntase libre de agregar un comentario o editar esta publicación si tiene más resultados de referencia de interés).
Algunos tiempos ...
Para las listas cortas, accumulate
es aproximadamente 4 veces más rápido:
from timeit import timeit
def sum1(l):
from itertools import accumulate
return list(accumulate(l))
def sum2(l):
from numpy import cumsum
return list(cumsum(l))
l = [1, 2, 3, 4, 5]
timeit(lambda: sum1(l), number=100000)
# 0.4243644131347537
timeit(lambda: sum2(l), number=100000)
# 1.7077815784141421
Para que las listas más largas se accumulate
es aproximadamente 3 veces más rápido:
l = [1, 2, 3, 4, 5]*1000
timeit(lambda: sum1(l), number=100000)
# 19.174508565105498
timeit(lambda: sum2(l), number=100000)
# 61.871223849244416
Si la array
numpy
no se numpy
a la list
, la accumulate
es todavía aproximadamente 2 veces más rápida:
from timeit import timeit
def sum1(l):
from itertools import accumulate
return list(accumulate(l))
def sum2(l):
from numpy import cumsum
return cumsum(l)
l = [1, 2, 3, 4, 5]*1000
print(timeit(lambda: sum1(l), number=100000))
# 19.18597290944308
print(timeit(lambda: sum2(l), number=100000))
# 37.759664884768426
Si coloca las importaciones fuera de las dos funciones y aún devuelve una array
numpy
, accumulate
es casi 2 veces más rápido:
from timeit import timeit
from itertools import accumulate
from numpy import cumsum
def sum1(l):
return list(accumulate(l))
def sum2(l):
return cumsum(l)
l = [1, 2, 3, 4, 5]*1000
timeit(lambda: sum1(l), number=100000)
# 19.042188624851406
timeit(lambda: sum2(l), number=100000)
# 35.17324400227517
Las expresiones de asignación de PEP 572 (esperadas para Python 3.8) ofrecen otra forma de resolver esto:
time_interval = [4, 6, 12]
total_time = 0
cum_time = [total_time := total_time + t for t in time_interval]
Mirad:
a = [4, 6, 12]
reduce(lambda c, x: c + [c[-1] + x], a, [0])[1:]
Será la salida (como se esperaba):
[4, 10, 22]
Primero, quieres una lista de subsecuencias en ejecución:
subseqs = (seq[:i] for i in range(1, len(seq)+1))
Entonces solo debes llamar a la sum
de cada subsecuencia:
sums = [sum(subseq) for subseq in subseqs]
(Esta no es la forma más eficiente de hacerlo, porque está agregando todos los prefijos repetidamente. Pero probablemente eso no importe en la mayoría de los casos de uso, y es más fácil de entender si no tiene que pensar en los totales acumulados).
Si está utilizando Python 3.2 o posterior, puede usar itertools.accumulate() para hacerlo por usted:
sums = itertools.accumulate(seq)
Y si está utilizando 3.1 o una versión anterior, puede simplemente copiar la fuente "equivalente a" directamente de los documentos (excepto para cambiar el next(it)
a it.next()
para 2.5 y it.next()
anteriores).
Prueba esto:
result = []
acc = 0
for i in time_interval:
acc += i
result.append(acc)
Respondiendo a la pregunta original preguntando cómo "resumir los números en una lista": revise la función sum () incorporada, probablemente haga lo que quiera.
Si estás haciendo mucho trabajo numérico con matrices como esta, te sugiero numpy
, que viene con una suma acumulativa de funciones cumsum
:
import numpy as np
a = [4,6,12]
np.cumsum(a)
#array([4, 10, 22])
Numpy es a menudo más rápido que el pitón puro para este tipo de cosas, ver en comparación con accumu de @ Ashwini :
In [136]: timeit list(accumu(range(1000)))
10000 loops, best of 3: 161 us per loop
In [137]: timeit list(accumu(xrange(1000)))
10000 loops, best of 3: 147 us per loop
In [138]: timeit np.cumsum(np.arange(1000))
100000 loops, best of 3: 10.1 us per loop
Pero, por supuesto, si es el único lugar que utilizará numpy, puede que no valga la pena tener una dependencia de él.
Si quieres una forma pythonic sin numpy trabajando en 2.7 esta sería mi forma de hacerlo
l = [1,2,3,4]
_d={-1:0}
cumsum=[_d.setdefault(idx, _d[idx-1]+item) for idx,item in enumerate(l)]
ahora probemos y probémoslo contra todas las demás implementaciones
import timeit
L=range(10000)
def sum1(l):
cumsum=[]
total = 0
for v in l:
total += v
cumsum.append(total)
return cumsum
def sum2(l):
import numpy as np
return list(np.cumsum(l))
def sum3(l):
return [sum(l[:i+1]) for i in xrange(len(l))]
def sum4(l):
return reduce(lambda c, x: c + [c[-1] + x], l, [0])[1:]
def this_implementation(l):
_d={-1:0}
return [_d.setdefault(idx, _d[idx-1]+item) for idx,item in enumerate(l)]
# sanity check
sum1(L)==sum2(L)==sum3(L)==sum4(L)==this_implementation(L)
>>> True
# PERFORMANCE TEST
timeit.timeit(''sum1(L)'',''from __main__ import sum1,sum2,sum3,sum4,this_implementation,L'', number=100)/100.
>>> 0.001018061637878418
timeit.timeit(''sum2(L)'',''from __main__ import sum1,sum2,sum3,sum4,this_implementation,L'', number=100)/100.
>>> 0.000829620361328125
timeit.timeit(''sum3(L)'',''from __main__ import sum1,sum2,sum3,sum4,this_implementation,L'', number=100)/100.
>>> 0.4606760001182556
timeit.timeit(''sum4(L)'',''from __main__ import sum1,sum2,sum3,sum4,this_implementation,L'', number=100)/100.
>>> 0.18932826995849608
timeit.timeit(''this_implementation(L)'',''from __main__ import sum1,sum2,sum3,sum4,this_implementation,L'', number=100)/100.
>>> 0.002348129749298096
Sin tener que usar Numpy, puede recorrer directamente la matriz y acumular la suma en el camino. Por ejemplo:
a=range(10)
i=1
while((i>0) & (i<10)):
a[i]=a[i-1]+a[i]
i=i+1
print a
Resultados en:
[0, 1, 3, 6, 10, 15, 21, 28, 36, 45]
In [42]: a = [4, 6, 12]
In [43]: [sum(a[:i+1]) for i in xrange(len(a))]
Out[43]: [4, 10, 22]
Esto es levemente más rápido que el método del generador anterior por @Ashwini para listas pequeñas
In [48]: %timeit list(accumu([4,6,12]))
100000 loops, best of 3: 2.63 us per loop
In [49]: %timeit [sum(a[:i+1]) for i in xrange(len(a))]
100000 loops, best of 3: 2.46 us per loop
Para listas más grandes, el generador es el camino a seguir con seguridad. . .
In [50]: a = range(1000)
In [51]: %timeit [sum(a[:i+1]) for i in xrange(len(a))]
100 loops, best of 3: 6.04 ms per loop
In [52]: %timeit list(accumu(a))
10000 loops, best of 3: 162 us per loop
def cummul_sum(list_arguement):
cumm_sum_lst = []
cumm_val = 0
for eachitem in list_arguement:
cumm_val += eachitem
cumm_sum_lst.append(cumm_val)
return cumm_sum_lst
lst = [4,6,12]
[sum(lst[:i+1]) for i in xrange(len(lst))]
Si está buscando una solución más eficiente (¿listas más grandes?), Un generador podría ser una buena numpy
(o simplemente usar numpy
si realmente le interesa el perf).
def gen(lst):
acu = 0
for num in lst:
yield num + acu
acu += num
print list(gen([4, 6, 12]))
values = [4, 6, 12]
total = 0
sums = []
for v in values:
total = total + v
sums.append(total)
print ''Values: '', values
print ''Sums: '', sums
Ejecutando este código da
Values: [4, 6, 12]
Sums: [4, 10, 22]