time_interval = [4, 6, 12]
Chcę podsumować liczby tak [4, 4+6, 4+6+12]
, aby uzyskać listę t = [4, 10, 22]
.
Próbowałem następujących rzeczy:
t1 = time_interval[0]
t2 = time_interval[1] + t1
t3 = time_interval[2] + t2
print(t1, t2, t3) # -> 4 10 22
python
list
sum
accumulate
user2259323
źródło
źródło
Odpowiedzi:
Jeśli wykonujesz dużo pracy numerycznej z takimi tablicami, proponuję
numpy
, która zawiera skumulowaną funkcję sumycumsum
:import numpy as np a = [4,6,12] np.cumsum(a) #array([4, 10, 22])
Numpy jest często szybszy niż czysty Python w tego rodzaju rzeczach, zobacz w porównaniu do @ Ashwini
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
Ale oczywiście, jeśli jest to jedyne miejsce, w którym będziesz używać numpy, może nie być warte uzależnienia od niego.
źródło
np.cumsun
przypadek, który zaczyna się od listy, aby uwzględnić czas konwersji.list
, nie polecamnumpy
.timeit
, "jeśli-n
nie jest podane, obliczana jest odpowiednia liczba pętli, próbując kolejnych potęg 10, aż całkowity czas wyniesie co najmniej 0,2 sekundy." Jeśli spodziewasz się, że to coś zmieni, możesz je dostarczyć,-n 1000
aby wszystkie były równoważne.W Pythonie 2 możesz zdefiniować własną funkcję generatora w następujący sposób:
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]
A w Pythonie 3.2+ możesz użyć
itertools.accumulate()
:In [1]: lis = [4,6,12] In [2]: from itertools import accumulate In [3]: list(accumulate(lis)) Out[3]: [4, 10, 22]
źródło
total = 0; partial_sums = [total := total + v for v in values]
. Nadal oczekiwałbym,accumulate
że będę szybszy.Ujrzeć:
a = [4, 6, 12] reduce(lambda c, x: c + [c[-1] + x], a, [0])[1:]
Wyświetli (zgodnie z oczekiwaniami):
[4, 10, 22]
źródło
c + [c[-1] + x]
w kółko sumuje się do całkowitego kwadratu czasu wykonywania w długości wejściowej.Zrobiłem test porównawczy dwóch najlepszych odpowiedzi w Pythonie 3.4 i stwierdziłem, że
itertools.accumulate
jest szybszy niżnumpy.cumsum
w wielu okolicznościach, często znacznie szybszy. Jednak, jak widać z komentarzy, nie zawsze tak jest i trudno jest wyczerpująco zbadać wszystkie opcje. (Jeśli masz dalsze interesujące wyniki testów porównawczych, możesz dodać komentarz lub edytować ten post).Niektóre czasy ...
W przypadku krótkich list
accumulate
jest około 4 razy szybszy: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
W przypadku dłuższych list
accumulate
jest około 3 razy szybszy:l = [1, 2, 3, 4, 5]*1000 timeit(lambda: sum1(l), number=100000) # 19.174508565105498 timeit(lambda: sum2(l), number=100000) # 61.871223849244416
Jeśli
numpy
array
nie jest rzucanylist
,accumulate
nadal jest około 2 razy szybszy: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
Jeśli umieścisz import poza dwiema funkcjami i nadal zwrócisz a
numpy
array
,accumulate
to nadal jest prawie 2 razy szybsze: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
źródło
list
z pięciu elementów, zwłaszcza jeśli nie chcesz przyjąćarray
w zamian. Gdyby lista, o której mowa, była naprawdę tak krótka, to ich czas wyświetlania byłby nieistotny - z pewnością dominowałyby zależności i czytelność. Jednak szerokie stosowanielist
jednolitego, znacznego typu danych numerycznych byłoby głupie; za to tępyarray
byłoby odpowiednie i zwykle szybsze.numpy
szybszy, chyba że coś przeoczyłem?sum2
funkcja jest prawdopodobnie przekształcanal
w tablicę. Wypróbuj czasa = np.array(l)
inp.cumsum(a)
osobno. Następnie spróbuja = np.tile(np.arange(1, 6), 1000)
vsl = [1,2,3,4,5]*1000
. W programie wykonującym inne procesy numeryczne (takie jak tworzenie lub ładowaniel
w pierwszej kolejności) dane robocze prawdopodobnie już znajdowałyby się w tablicy, a ich tworzenie wiązałoby się ze stałym kosztem.Spróbuj tego: funkcja akumuluj razem z operatorem add wykonuje bieżące dodawanie.
import itertools import operator result = itertools.accumulate([1,2,3,4,5], operator.add) list(result)
źródło
operator.add
ponieważ domyślną operacją i tak jest dodawanie.Wyrażenia przypisania z PEP 572 (nowość w Pythonie 3.8) oferują jeszcze inny sposób rozwiązania tego problemu:
time_interval = [4, 6, 12] total_time = 0 cum_time = [total_time := total_time + t for t in time_interval]
źródło
Możesz obliczyć skumulowaną listę sum w czasie liniowym za pomocą prostej
for
pętli: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]
Biblioteka standardowa
itertools.accumulate
może być szybszą alternatywą (ponieważ jest zaimplementowana w C):from itertools import accumulate time_interval = [4, 6, 12] print(list(accumulate(time_interval))) # [4, 10, 22]
źródło
values = [4, 6, 12] total = 0 sums = [] for v in values: total = total + v sums.append(total) print 'Values: ', values print 'Sums: ', sums
Uruchomienie tego kodu daje
Values: [4, 6, 12] Sums: [4, 10, 22]
źródło
W Pythonie3, aby znaleźć skumulowaną sumę listy, gdzie ten
i
element jest sumą pierwszych elementów i + 1 z oryginalnej listy, możesz wykonać:a = [4 , 6 , 12] b = [] for i in range(0,len(a)): b.append(sum(a[:i+1])) print(b)
LUB możesz użyć rozumienia z listy:
b = [sum(a[:x+1]) for x in range(0,len(a))]
Wynik
[4,10,22]
źródło
Jeśli chcesz mieć pythonowy sposób bez numpy pracy w 2.7, to byłby mój sposób na zrobienie tego
l = [1,2,3,4] _d={-1:0} cumsum=[_d.setdefault(idx, _d[idx-1]+item) for idx,item in enumerate(l)]
teraz wypróbujmy to i przetestujmy z wszystkimi innymi implementacjami
import timeit, sys L=list(range(10000)) if sys.version_info >= (3, 0): reduce = functools.reduce xrange = range 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
źródło
Odpowiedzi na to może być wiele w zależności od długości listy i wyników. Jeden bardzo prosty sposób, który mogę pomyśleć, nie myśląc o spektaklu, jest następujący:
a = [1, 2, 3, 4] a = [sum(a[0:x:1]) for x in range(len(a)+1)][1:] print(a)
[1, 3, 6, 10]
Dzieje się to za pomocą rozumienia list i może to działać całkiem nieźle, po prostu tutaj wiele razy dodam podtablicę, możesz prawdopodobnie improwizować i uprościć!
Pozdrawiam twoje przedsięwzięcie!
źródło
Po pierwsze, chcesz mieć bieżącą listę podciągów:
subseqs = (seq[:i] for i in range(1, len(seq)+1))
Następnie po prostu wywołujesz
sum
każdy podciąg:sums = [sum(subseq) for subseq in subseqs]
(Nie jest to najskuteczniejszy sposób, aby to zrobić, ponieważ wielokrotnie dodajesz wszystkie przedrostki. Ale to prawdopodobnie nie ma znaczenia w większości przypadków użycia i łatwiej jest to zrozumieć, jeśli nie musisz myśleć o sumy bieżące).
Jeśli używasz Pythona 3.2 lub nowszego, możesz
itertools.accumulate
zrobić to za siebie:Jeśli używasz wersji 3.1 lub wcześniejszej, możesz po prostu skopiować źródło „odpowiednika” bezpośrednio z dokumentów (z wyjątkiem zmiany
next(it)
na wersjęit.next()
2.5 i starszą).źródło
range
niż hakować wokół, robiąc[1:]
na koniec, lub ignorować.)[4,6,12]
ponieważ, jak napisał w pytaniu, już wie, co to jest!Spróbuj tego:
result = [] acc = 0 for i in time_interval: acc += i result.append(acc)
źródło
In [42]: a = [4, 6, 12] In [43]: [sum(a[:i+1]) for i in xrange(len(a))] Out[43]: [4, 10, 22]
Jest to trochę szybsze niż metoda generatora powyżej autorstwa @Ashwini dla małych list
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
W przypadku większych list z pewnością dobrym rozwiązaniem jest generator. . .
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
źródło
Trochę hacky, ale wydaje się działać:
def cumulative_sum(l): y = [0] def inc(n): y[0] += n return y[0] return [inc(x) for x in l]
Myślałem, że funkcja wewnętrzna byłaby w stanie zmodyfikować
y
zadeklarowany w zewnętrznym zakresie leksykalnym, ale to nie zadziałało, więc zamiast tego gramy w paskudne hacki z modyfikacją struktury. Prawdopodobnie bardziej eleganckie jest użycie generatora.źródło
Bez konieczności używania Numpy, możesz wykonać pętlę bezpośrednio nad tablicą i po drodze gromadzić sumę. Na przykład:
a=range(10) i=1 while((i>0) & (i<10)): a[i]=a[i-1]+a[i] i=i+1 print a
Prowadzi do:
[0, 1, 3, 6, 10, 15, 21, 28, 36, 45]
źródło
Czysty oneliner w pytonie dla sumy skumulowanej:
cumsum = lambda X: X[:1] + cumsum([X[0]+X[1]] + X[2:]) if X[1:] else X
Jest to wersja rekurencyjna inspirowana rekursywnymi sumami kumulacyjnymi . Kilka wyjaśnień:
X[:1]
jest listą zawierającą poprzedni element i jest prawie taki sam jak[X[0]]
(który narzekałby na puste listy).cumsum
Wywołanie rekurencyjne w drugim członie przetwarza bieżący element[1]
i pozostałą listę, których długość zostanie zmniejszona o jeden.if X[1:]
jest krótszy dlaif len(X)>1
.Test:
cumsum([4,6,12]) #[4, 10, 22] cumsum([]) #[]
I podobnie dla produktu zbiorczego:
cumprod = lambda X: X[:1] + cumprod([X[0]*X[1]] + X[2:]) if X[1:] else X
Test:
cumprod([4,6,12]) #[4, 24, 288]
źródło
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))
źródło
Oto kolejne zabawne rozwiązanie. Wykorzystuje to
locals()
dyktowanie rozumienia, czyli zmienne lokalne generowane wewnątrz zakresu rozumienia listy:>>> [locals().setdefault(i, (elem + locals().get(i-1, 0))) for i, elem in enumerate(time_interval)] [4, 10, 22]
Oto, jak
locals()
wygląda każda iteracja:>>> [[locals().setdefault(i, (elem + locals().get(i-1, 0))), locals().copy()][1] for i, elem in enumerate(time_interval)] [{'.0': <enumerate at 0x21f21f7fc80>, 'i': 0, 'elem': 4, 0: 4}, {'.0': <enumerate at 0x21f21f7fc80>, 'i': 1, 'elem': 6, 0: 4, 1: 10}, {'.0': <enumerate at 0x21f21f7fc80>, 'i': 2, 'elem': 12, 0: 4, 1: 10, 2: 22}]
Wydajność nie jest straszna w przypadku małych list:
>>> %timeit list(accumulate([4, 6, 12])) 387 ns ± 7.53 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each) >>> %timeit np.cumsum([4, 6, 12]) 5.31 µs ± 67.8 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each) >>> %timeit [locals().setdefault(i, (e + locals().get(i-1,0))) for i,e in enumerate(time_interval)] 1.57 µs ± 12 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
I oczywiście wypada płasko w przypadku większych list.
>>> l = list(range(1_000_000)) >>> %timeit list(accumulate(l)) 95.1 ms ± 5.22 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) >>> %timeit np.cumsum(l) 79.3 ms ± 1.07 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) >>> %timeit np.cumsum(l).tolist() 120 ms ± 1.23 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) >>> %timeit [locals().setdefault(i, (e + locals().get(i-1, 0))) for i, e in enumerate(l)] 660 ms ± 5.14 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Chociaż metoda jest brzydka i niepraktyczna, z pewnością jest zabawna.
źródło
lst = [4,6,12] [sum(lst[:i+1]) for i in xrange(len(lst))]
Jeśli szukasz bardziej wydajnego rozwiązania (większych list?), Dobrym rozwiązaniem może być generator (lub po prostu użyj,
numpy
jeśli naprawdę zależy Ci na perf).def gen(lst): acu = 0 for num in lst: yield num + acu acu += num print list(gen([4, 6, 12]))
źródło
Byłoby to w stylu Haskella:
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])
źródło