time_interval = [4, 6, 12]
Quero somar os números [4, 4+6, 4+6+12]
para obter a lista t = [4, 10, 22]
.
Tentei o seguinte:
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
fonte
fonte
Respostas:
Se você estiver fazendo muito trabalho numérico com matrizes como esta, sugiro
numpy
, que vem com uma função de soma cumulativacumsum
:import numpy as np a = [4,6,12] np.cumsum(a) #array([4, 10, 22])
O Numpy costuma ser mais rápido do que o Python puro para esse tipo de coisa, veja em comparação com @ 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
Mas é claro que se for o único lugar onde você usará o numpy, pode não valer a pena depender dele.
fonte
np.cumsun
case que começa com uma lista, para levar em consideração o tempo de conversão.list
eu não recomendonumpy
.timeit
, "se-n
não for fornecido, um número adequado de loops é calculado tentando potências sucessivas de 10 até que o tempo total seja de pelo menos 0,2 segundos." Se você espera que faça diferença, pode fornecer-n 1000
para torná-los todos equivalentes.No Python 2, você pode definir sua própria função de gerador assim:
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]
E no Python 3.2+ você pode 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]
fonte
total = 0; partial_sums = [total := total + v for v in values]
. Eu ainda esperariaaccumulate
ser mais rápido.Ver:
a = [4, 6, 12] reduce(lambda c, x: c + [c[-1] + x], a, [0])[1:]
Irá produzir (conforme esperado):
[4, 10, 22]
fonte
c + [c[-1] + x]
repetidas vezes soma um tempo de execução total quadrático no comprimento de entrada.Eu fiz uma avaliação comparativa das duas principais respostas com Python 3.4 e descobri que
itertools.accumulate
é mais rápido do quenumpy.cumsum
em muitas circunstâncias, geralmente muito mais rápido. No entanto, como você pode ver nos comentários, nem sempre é esse o caso e é difícil explorar exaustivamente todas as opções. (Sinta-se à vontade para adicionar um comentário ou editar esta postagem se tiver mais resultados de referência de interesse.)Alguns horários ...
Para listas curtas
accumulate
é cerca de 4 vezes mais 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 listas mais longas,
accumulate
é cerca de 3 vezes mais 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
Se
numpy
array
não for lançadolist
,accumulate
ainda é cerca de 2 vezes mais 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 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
Se você colocar as importações fora das duas funções e ainda retornar a
numpy
array
,accumulate
ainda será quase 2 vezes mais 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
fonte
list
dos cinco itens, especialmente se não estiver disposto a aceitar umarray
em troca. Se a lista em questão for realmente tão curta, então seu tempo de execução seria inconseqüente - dependências e legibilidade certamente dominariam. Mas o amplo uso de umlist
tipo de dado numérico uniforme de comprimento significativo seria bobo; para isso, um numpyarray
seria apropriado e geralmente mais rápido.numpy
mais rápido, a menos que tenha esquecido algo?sum2
função provavelmente está na conversãol
em um array. Experimente o tempoa = np.array(l)
enp.cumsum(a)
separadamente. Então tentea = np.tile(np.arange(1, 6), 1000)
vsl = [1,2,3,4,5]*1000
. Em um programa conduzindo outros processos numéricos (como a criação ou o carregamento del
em primeiro lugar), seus dados de trabalho provavelmente já estariam em um array, e a criação seria um custo constante.Tente isto: a função de acumulação, junto com a adição do operador, executa a adição em execução.
import itertools import operator result = itertools.accumulate([1,2,3,4,5], operator.add) list(result)
fonte
operator.add
porque a operação padrão é adição de qualquer maneira.As expressões de atribuição do PEP 572 (novo no Python 3.8) oferecem outra maneira de resolver isso:
time_interval = [4, 6, 12] total_time = 0 cum_time = [total_time := total_time + t for t in time_interval]
fonte
Você pode calcular a lista de soma cumulativa em tempo linear com um
for
loop simples :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]
A biblioteca padrão
itertools.accumulate
pode ser uma alternativa mais rápida (já que é implementada em C):from itertools import accumulate time_interval = [4, 6, 12] print(list(accumulate(time_interval))) # [4, 10, 22]
fonte
Se você quiser uma forma pitônica sem o trabalho entorpecido no 2.7, esta seria a minha maneira de fazê-lo
l = [1,2,3,4] _d={-1:0} cumsum=[_d.setdefault(idx, _d[idx-1]+item) for idx,item in enumerate(l)]
agora vamos tentar e testar contra todas as outras implementações
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
fonte
Pode haver muitas respostas para isso, dependendo do tamanho da lista e do desempenho. Uma maneira muito simples que posso pensar sem pensar na performance é esta:
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]
Isso é feito usando a compreensão de lista e isso pode funcionar muito bem, mas aqui estou adicionando o subarray muitas vezes, você poderia improvisar sobre isso e torná-lo simples!
Felicidades pelo seu esforço!
fonte
values = [4, 6, 12] total = 0 sums = [] for v in values: total = total + v sums.append(total) print 'Values: ', values print 'Sums: ', sums
Executar este código dá
Values: [4, 6, 12] Sums: [4, 10, 22]
fonte
Em Python3, para encontrar a soma cumulativa de uma lista em que o
i
ésimo elemento é a soma dos primeiros i + 1 elementos da lista original, você pode fazer:a = [4 , 6 , 12] b = [] for i in range(0,len(a)): b.append(sum(a[:i+1])) print(b)
OU você pode usar a compreensão de lista:
b = [sum(a[:x+1]) for x in range(0,len(a))]
Resultado
[4,10,22]
fonte
Primeiro, você quer uma lista contínua de subsequências:
subseqs = (seq[:i] for i in range(1, len(seq)+1))
Então você apenas chama
sum
cada subsequência:sums = [sum(subseq) for subseq in subseqs]
(Esta não é a maneira mais eficiente de fazer isso, porque você está adicionando todos os prefixos repetidamente. Mas isso provavelmente não importa para a maioria dos casos de uso e é mais fácil de entender se você não tiver que pensar em os totais corridos.)
Se estiver usando Python 3.2 ou mais recente, você pode usar
itertools.accumulate
para fazer isso por você:E se você estiver usando 3.1 ou anterior, você pode simplesmente copiar o "equivalente a" fonte em linha reta fora dos docs (exceto para mudar
next(it)
ait.next()
para 2,5 e anteriores).fonte
range
do que hackear[1:]
no final, ou ignorá-lo.)[4,6,12]
, pois, como ele escreveu na pergunta, ele já sabe o que é!Experimente isto:
result = [] acc = 0 for i in time_interval: acc += i result.append(acc)
fonte
In [42]: a = [4, 6, 12] In [43]: [sum(a[:i+1]) for i in xrange(len(a))] Out[43]: [4, 10, 22]
Esta é slighlty mais rápido do que o método gerador acima por @Ashwini para pequenas listas
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 maiores, o gerador é o caminho certo. . .
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
fonte
Um pouco hacky, mas parece funcionar:
def cumulative_sum(l): y = [0] def inc(n): y[0] += n return y[0] return [inc(x) for x in l]
Eu realmente pensei que a função interna seria capaz de modificar o
y
declarado no escopo léxico externo, mas isso não funcionou, então nós jogamos alguns hacks desagradáveis com modificação de estrutura. Provavelmente é mais elegante usar um gerador.fonte
Sem ter que usar o Numpy, você pode fazer um loop diretamente sobre a matriz e acumular a soma ao longo do caminho. Por exemplo:
a=range(10) i=1 while((i>0) & (i<10)): a[i]=a[i-1]+a[i] i=i+1 print a
Resulta em:
[0, 1, 3, 6, 10, 15, 21, 28, 36, 45]
fonte
Um oneliner python puro para soma cumulativa:
cumsum = lambda X: X[:1] + cumsum([X[0]+X[1]] + X[2:]) if X[1:] else X
Esta é uma versão recursiva inspirada em somas cumulativas recursivas . Algumas explicações:
X[:1]
é uma lista contendo o elemento anterior e é quase o mesmo que[X[0]]
(o que reclamaria de listas vazias).cumsum
chamada recursiva no segundo termo processa o elemento atual[1]
e a lista restante, cujo comprimento será reduzido em um.if X[1:]
é mais curto paraif len(X)>1
.Teste:
cumsum([4,6,12]) #[4, 10, 22] cumsum([]) #[]
E semelhante para produto cumulativo:
cumprod = lambda X: X[:1] + cumprod([X[0]*X[1]] + X[2:]) if X[1:] else X
Teste:
cumprod([4,6,12]) #[4, 24, 288]
fonte
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))
fonte
Aqui está outra solução divertida. Isso aproveita o
locals()
ditado de uma compreensão, ou seja, variáveis locais geradas dentro do escopo de compreensão da lista:>>> [locals().setdefault(i, (elem + locals().get(i-1, 0))) for i, elem in enumerate(time_interval)] [4, 10, 22]
Aqui está o que ele
locals()
procura para cada iteração:>>> [[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}]
O desempenho não é terrível para listas pequenas:
>>> %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)
E, obviamente, não dá certo para listas maiores.
>>> 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)
Mesmo que o método seja feio e pouco prático, com certeza é divertido.
fonte
lst = [4,6,12] [sum(lst[:i+1]) for i in xrange(len(lst))]
Se você estiver procurando por uma solução mais eficiente (listas maiores?), Um gerador pode ser uma boa opção (ou apenas use
numpy
se você realmente se preocupa com o desempenho).def gen(lst): acu = 0 for num in lst: yield num + acu acu += num print list(gen([4, 6, 12]))
fonte
Isso seria no 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])
fonte