time_interval = [4, 6, 12]
Je veux résumer les nombres tels que [4, 4+6, 4+6+12]
afin d'obtenir la liste t = [4, 10, 22]
.
J'ai essayé ce qui suit:
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
Si vous faites beaucoup de travail numérique avec des tableaux comme celui-ci, je suggérerais numpy
, qui vient avec une fonction de somme cumulée cumsum
:
import numpy as np
a = [4,6,12]
np.cumsum(a)
#array([4, 10, 22])
Numpy est souvent plus rapide que le python pur pour ce genre de chose, à comparer avec @ Ashwini's accumu
:
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
Mais bien sûr, si c’est le seul endroit où vous utiliserez numpy, il ne vaut peut-être pas la peine d’en dépendre.
En Python 2, vous pouvez définir votre propre fonction de générateur comme ceci:
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]
Et dans Python 3.2+, vous pouvez utiliser itertools.accumulate()
:
In [1]: lis = [4,6,12]
In [2]: from itertools import accumulate
In [3]: list(accumulate(lis))
Out[3]: [4, 10, 22]
Voir:
a = [4, 6, 12]
reduce(lambda c, x: c + [c[-1] + x], a, [0])[1:]
Sortie (comme prévu):
[4, 10, 22]
J'ai comparé les deux premières réponses avec Python 3.4 et j'ai constaté que itertools.accumulate
est plus rapide que numpy.cumsum
dans de nombreuses circonstances, souvent beaucoup plus rapidement. Cependant, comme vous pouvez le constater dans les commentaires, il se peut que cela ne soit pas toujours le cas et qu'il soit difficile d'explorer toutes les options de manière exhaustive. (N'hésitez pas à ajouter un commentaire ou à modifier ce message si vous avez d'autres résultats de référence intéressants.)
Quelques timings ...
accumulate
est environ 4 fois plus rapide:
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
accumulate
est environ 3 fois plus rapide pour les listes plus longues:
l = [1, 2, 3, 4, 5]*1000
timeit(lambda: sum1(l), number=100000)
# 19.174508565105498
timeit(lambda: sum2(l), number=100000)
# 61.871223849244416
Si numpy
array
n'est pas converti en list
, accumulate
reste environ deux fois plus rapide:
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 vous placez les importations en dehors des deux fonctions et que vous retournez quand même une numpy
array
, accumulate
reste presque 2 fois plus rapide:
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
Tout d'abord, vous voulez une liste courante de sous-séquences:
subseqs = (seq[:i] for i in range(1, len(seq)+1))
Ensuite, vous appelez simplement sum
sur chaque sous-séquence:
sums = [sum(subseq) for subseq in subseqs]
(Ce n’est pas le moyen le plus efficace de le faire, car vous ajoutez tous les préfixes à plusieurs reprises. Mais cela n’aura probablement pas d’importance pour la plupart des cas d’utilisation, et il est plus facile de comprendre si vous n’aurez pas à penser à les totaux cumulés.)
Si vous utilisez Python 3.2 ou une version plus récente, vous pouvez utiliser itertools.accumulate
pour le faire à votre place:
sums = itertools.accumulate(seq)
Et si vous utilisez la version 3.1 ou une version antérieure, vous pouvez simplement copier la source "équivalente à" directement dans la documentation (à l'exception de la modification de next(it)
en it.next()
pour les versions 2.5 et antérieures).
Si vous voulez une méthode Pythonic sans Numpy dans la version 2.7, ce serait ma façon de le faire.
l = [1,2,3,4]
_d={-1:0}
cumsum=[_d.setdefault(idx, _d[idx-1]+item) for idx,item in enumerate(l)]
maintenant essayons et testons-le contre toutes les autres implémentations
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
values = [4, 6, 12]
total = 0
sums = []
for v in values:
total = total + v
sums.append(total)
print 'Values: ', values
print 'Sums: ', sums
Exécuter ce code donne
Values: [4, 6, 12]
Sums: [4, 10, 22]
Les expressions d'affectation de PEP 572 (attendues pour Python 3.8) offrent un autre moyen de résoudre ce problème:
time_interval = [4, 6, 12]
total_time = 0
cum_time = [total_time := total_time + t for t in time_interval]
Essaye ça:
result = []
acc = 0
for i in time_interval:
acc += i
result.append(acc)
Un peu hacky, mais semble fonctionner:
def cumulative_sum(l):
y = [0]
def inc(n):
y[0] += n
return y[0]
return [inc(x) for x in l]
Je pensais que la fonction interne serait capable de modifier la variable y
déclarée dans la portée lexicale externe, mais cela n'a pas fonctionné, nous avons donc joué à de vilains stratagèmes avec modification de structure. Il est probablement plus élégant d'utiliser un générateur.
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
En Python3, pour trouver la somme cumulative d'une liste où l'élément i
th est la somme des premiers éléments i + 1 de la liste d'origine, vous pouvez effectuer les opérations suivantes:
a = [4 , 6 , 12]
b = []
for i in range(0,len(a)):
b.append(sum(a[:i+1]))
print(b)
OU vous pouvez utiliser la compréhension de liste:
b = [sum(a[:x+1]) for x in range(0,len(a))]
sortie
[4,10,22]
lst = [4,6,12]
[sum(lst[:i+1]) for i in xrange(len(lst))]
Si vous recherchez une solution plus efficace (de plus grandes listes?), Un générateur peut être un bon appel (ou utilisez simplement numpy
si vous vous souciez vraiment de perf).
def gen(lst):
acu = 0
for num in lst:
yield num + acu
acu += num
print list(gen([4, 6, 12]))
Essaye ça: fonction accumuler, avec opérateur add effectue l’ajout en cours.
import itertools
import operator
result = itertools.accumulate([1,2,3,4,5], operator.add)
list(result)
In [42]: a = [4, 6, 12]
In [43]: [sum(a[:i+1]) for i in xrange(len(a))]
Out[43]: [4, 10, 22]
C'est légèrement plus rapide que la méthode du générateur ci-dessus de @Ashwini pour les petites listes
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
Pour les plus grandes listes, le générateur est la voie à suivre à coup sûr. . .
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
Un oneliner en python pur pour une somme cumulée:
cumsum = lambda X: X[:1] + cumsum([X[0]+X[1]] + X[2:]) if X[1:] else X
Ceci est une version récursive inspirée par sommes cumulatives récursives . Quelques explications:
X[:1]
est une liste contenant l’élément précédent et est presque identique à [X[0]]
(qui se plaindrait des listes vides).cumsum
du second terme traite l'élément actuel [1]
et la liste restante dont la longueur sera réduite de un.if X[1:]
est plus court pour if len(X)>1
.Tester:
cumsum([4,6,12])
#[4, 10, 22]
cumsum([])
#[]
Et simular pour le produit cumulatif:
cumprod = lambda X: X[:1] + cumprod([X[0]*X[1]] + X[2:]) if X[1:] else X
Tester:
cumprod([4,6,12])
#[4, 24, 288]
Vous pouvez calculer la liste des sommes cumulées en temps linéaire avec une simple boucle for
:
def csum(lst):
s = lst.copy()
for i in range(1, len(s)):
s[i] += s[i-1]
return s
time_interval = [4, 6, 12]
print(csum(time_interval)) # [4, 10, 22]
La bibliothèque standard itertools.accumulate
peut être une alternative plus rapide (puisqu'elle est implémentée en C):
from itertools import accumulate
time_interval = [4, 6, 12]
print(list(accumulate(time_interval))) # [4, 10, 22]
Sans avoir à utiliser Numpy, vous pouvez effectuer une boucle directe sur le tableau et accumuler la somme en cours de route. Par exemple:
a=range(10)
i=1
while((i>0) & (i<10)):
a[i]=a[i-1]+a[i]
i=i+1
print a
Résulte en:
[0, 1, 3, 6, 10, 15, 21, 28, 36, 45]
l = [1,-1,3]
cum_list = l
def sum_list(input_list):
index = 1
for i in input_list[1:]:
cum_list[index] = i + input_list[index-1]
index = index + 1
return cum_list
print(sum_list(l))