time_interval = [4, 6, 12]
Я хочу суммировать цифры [4, 4+6, 4+6+12], чтобы получить список t = [4, 10, 22].
Я пробовал следующее:
t1 = time_interval[0]
t2 = time_interval[1] + t1
t3 = time_interval[2] + t2
print(t1, t2, t3) # -> 4 10 22
time_interval = [4, 6, 12]
Я хочу суммировать цифры [4, 4+6, 4+6+12], чтобы получить список t = [4, 10, 22].
Я пробовал следующее:
t1 = time_interval[0]
t2 = time_interval[1] + t1
t3 = time_interval[2] + t2
print(t1, t2, t3) # -> 4 10 22
Ответы:
Если вы много работаете с числами с подобными массивами, я бы посоветовал numpyвоспользоваться функцией накопительной суммы cumsum:
import numpy as np
a = [4,6,12]
np.cumsum(a)
#array([4, 10, 22])
Numpy часто быстрее, чем чистый питон для такого рода вещей, см. По сравнению с @ Ashwiniaccumu :
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
Но, конечно, если это единственное место, где вы будете использовать numpy, возможно, не стоит зависеть от него.
np.cumsunдело, которое начинается со списка, чтобы учесть время преобразования.
listя бы не рекомендовал numpy.
В Python 2 вы можете определить свою собственную функцию генератора следующим образом:
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]
А в Python 3.2+ вы можете использовать itertools.accumulate():
In [1]: lis = [4,6,12]
In [2]: from itertools import accumulate
In [3]: list(accumulate(lis))
Out[3]: [4, 10, 22]
total = 0; partial_sums = [total := total + v for v in values]. Я все равно ожидал accumulateбы быть быстрее.
Вот:
a = [4, 6, 12]
reduce(lambda c, x: c + [c[-1] + x], a, [0])[1:]
Выведет (как и ожидалось):
[4, 10, 22]
c + [c[-1] + x]снова и снова составляют общее время выполнения, квадратичное по длине ввода.
Я провел тест двух лучших ответов с Python 3.4 и обнаружил, что itertools.accumulateон быстрее, чем numpy.cumsumпри многих обстоятельствах, часто намного быстрее. Однако, как видно из комментариев, это может быть не всегда, и исчерпывающе изучить все варианты сложно. (Не стесняйтесь добавить комментарий или отредактировать этот пост, если у вас есть интересующие результаты тестов.)
Некоторое время ...
Для коротких списков accumulateпримерно в 4 раза быстрее:
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примерно в 3 раза быстрее:
l = [1, 2, 3, 4, 5]*1000
timeit(lambda: sum1(l), number=100000)
# 19.174508565105498
timeit(lambda: sum2(l), number=100000)
# 61.871223849244416
Если numpy arrayне приведено к list, accumulateвсе равно примерно в 2 раза быстрее:
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
Если вы поместите импорт за пределы двух функций и все равно вернете a numpy array, accumulateвсе равно будет почти в 2 раза быстрее:
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
listпяти элементов, особенно если вы не хотите принимать arrayвзамен. Если рассматриваемый список действительно такой короткий, то время их выполнения будет несущественным - зависимости и удобочитаемость, безусловно, будут преобладать. Но широкое использование listединого числового типа данных значительной длины было бы глупо; для этого array будет подходящим numpy , и обычно быстрее.
numpyбыть быстрее, если я что-то не упустил?
sum2функции, вероятно, заключается в преобразовании lв массив. Попробуй тайминг a = np.array(l)и np.cumsum(a)отдельно. Тогда попробуйте a = np.tile(np.arange(1, 6), 1000)против l = [1,2,3,4,5]*1000. В программе, выполняющей другие числовые процессы (например, создание или загрузка lв первую очередь), ваши рабочие данные, вероятно, уже будут в массиве, и создание будет стоить постоянную стоимость.
Попробуйте следующее: функция накопления вместе с оператором add выполняет текущее сложение.
import itertools
import operator
result = itertools.accumulate([1,2,3,4,5], operator.add)
list(result)
operator.addпоскольку операция по умолчанию в любом случае является добавлением.
Выражения присваивания из PEP 572 (новое в Python 3.8) предлагают еще один способ решить эту проблему:
time_interval = [4, 6, 12]
total_time = 0
cum_time = [total_time := total_time + t for t in time_interval]
Вы можете рассчитать совокупный список сумм за линейное время с помощью простого 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]
Стандартная библиотека itertools.accumulateможет быть более быстрой альтернативой (поскольку она реализована на C):
from itertools import accumulate
time_interval = [4, 6, 12]
print(list(accumulate(time_interval))) # [4, 10, 22]
values = [4, 6, 12]
total = 0
sums = []
for v in values:
total = total + v
sums.append(total)
print 'Values: ', values
print 'Sums: ', sums
Запуск этого кода дает
Values: [4, 6, 12]
Sums: [4, 10, 22]
В Python3, чтобы найти кумулятивную сумму списка, где ith элемент является суммой первых элементов i + 1 из исходного списка, вы можете сделать:
a = [4 , 6 , 12]
b = []
for i in range(0,len(a)):
b.append(sum(a[:i+1]))
print(b)
ИЛИ вы можете использовать понимание списка:
b = [sum(a[:x+1]) for x in range(0,len(a))]
Выход
[4,10,22]
Если вам нужен питонический способ без работы numpy в 2.7, это был бы мой способ сделать это
l = [1,2,3,4]
_d={-1:0}
cumsum=[_d.setdefault(idx, _d[idx-1]+item) for idx,item in enumerate(l)]
теперь давайте попробуем и проверим на всех других реализациях
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
На это может быть много ответов, в зависимости от длины списка и производительности. Один очень простой способ, который я могу думать, не думая о производительности, заключается в следующем:
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]
Это происходит с использованием понимания списка, и это может работать довольно хорошо, просто здесь я добавляю много раз над подмассивом, вы могли бы импровизировать с этим и упростить!
Приветствую вас!
Во-первых, вам нужен текущий список подпоследовательностей:
subseqs = (seq[:i] for i in range(1, len(seq)+1))
Затем вы просто вызываете sumкаждую подпоследовательность:
sums = [sum(subseq) for subseq in subseqs]
(Это не самый эффективный способ сделать это, потому что вы постоянно добавляете все префиксы. Но это, вероятно, не имеет значения для большинства случаев использования, и его легче понять, если вам не нужно думать о промежуточные итоги.)
Если вы используете Python 3.2 или новее, вы можете использовать itertools.accumulateдля этого:
sums = itertools.accumulate(seq)
А если вы используете 3.1 или более раннюю версию, вы можете просто скопировать "эквивалентный" исходный код прямо из документации (за исключением перехода на 2.5 и более ранние next(it)версии it.next()).
rangeчем обойти это, делая [1:]в конце, или игнорировать это.)
[4,6,12]поскольку, как он написал в вопросе, он уже знает, что это такое!
In [42]: a = [4, 6, 12]
In [43]: [sum(a[:i+1]) for i in xrange(len(a))]
Out[43]: [4, 10, 22]
Это немного быстрее, чем метод генератора выше @Ashwini для небольших списков
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
Для больших списков обязательно используйте генератор. . .
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 cumulative_sum(l):
y = [0]
def inc(n):
y[0] += n
return y[0]
return [inc(x) for x in l]
Я действительно думал, что внутренняя функция сможет изменить yобъявленное во внешней лексической области видимости, но это не сработало, поэтому вместо этого мы используем несколько неприятных хаков с модификацией структуры. Возможно, более элегантно использовать генератор.
Без использования Numpy вы можете перебирать массив напрямую и накапливать сумму по пути. Например:
a=range(10)
i=1
while((i>0) & (i<10)):
a[i]=a[i-1]+a[i]
i=i+1
print a
Результаты в:
[0, 1, 3, 6, 10, 15, 21, 28, 36, 45]
Чистый питон oneliner для накопительной суммы:
cumsum = lambda X: X[:1] + cumsum([X[0]+X[1]] + X[2:]) if X[1:] else X
Это рекурсивная версия, вдохновленная рекурсивными совокупными суммами . Некоторые пояснения:
X[:1]- это список, содержащий предыдущий элемент, и он почти такой же, как [X[0]](который будет жаловаться на пустые списки).cumsumвызов во втором члене обрабатывает текущий элемент [1]и оставшийся список, длина которого будет уменьшена на единицу.if X[1:]короче для if len(X)>1.Контрольная работа:
cumsum([4,6,12])
#[4, 10, 22]
cumsum([])
#[]
И моделирование совокупного продукта:
cumprod = lambda X: X[:1] + cumprod([X[0]*X[1]] + X[2:]) if X[1:] else X
Контрольная работа:
cumprod([4,6,12])
#[4, 24, 288]
Вот еще одно забавное решение. Это использует преимущество locals()понимания, то есть локальные переменные, сгенерированные внутри области понимания списка:
>>> [locals().setdefault(i, (elem + locals().get(i-1, 0))) for i, elem
in enumerate(time_interval)]
[4, 10, 22]
Вот как locals()выглядит каждая итерация:
>>> [[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}]
Производительность не страшна для небольших списков:
>>> %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)
И, очевидно, не подходит для больших списков.
>>> 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)
Несмотря на то, что этот метод уродлив и непрактичен, он определенно забавен.
lst = [4,6,12]
[sum(lst[:i+1]) for i in xrange(len(lst))]
Если вы ищете более эффективное решение (большие списки?), Генератор может быть хорошим numpyвыбором (или просто используйте, если вы действительно заботитесь о производительности).
def gen(lst):
acu = 0
for num in lst:
yield num + acu
acu += num
print list(gen([4, 6, 12]))