Reputation: 375
Say I have list, list_a = [100, 5, 1, 2, 200, 3, 1, 300, 6, 6]
And another list, ind_list = [0, 4, 7]
I want to create a third list that will contain the cumulative sum of the first list which "resets" on every index from ind_list
.
To clarify, the result should be res_list = [100, 105, 106, 108, 200, 203, 204, 300, 306, 312]
Upvotes: 10
Views: 894
Reputation: 4137
A slightly simpler answer only using NumPy:
import numpy as np
list_a = [100, 5, 1, 2, 200, 3, 1, 300, 6, 6]
ind_list = [2, 4, 7]
res_list = []
for i, n in enumerate(ind_list):
if i == 0:
n_prev=0
else:
n_prev = ind_list[i-1]
for k in range(n-n_prev):
res_list.append(np.sum(list_a[n_prev:n_prev+k+1]))
if i == len(ind_list)-1:
for k in range(len(list_a)-n):
res_list.append(np.sum(list_a[n:n+k+1]))
print(res_list)
[100, 105, 106, 108, 200, 203, 204, 300, 306, 312]
Upvotes: 1
Reputation: 46899
Or just create a generator:
def generate_running_sum(summands, index_list):
current_sum = 0
for i, summand in enumerate(summands):
if i in set(index_list):
current_sum = 0
current_sum += summand
yield current_sum
Applied to your data:
list_a = [100, 5, 1, 2, 200, 3, 1, 300, 6, 6]
ind_list = [0, 4, 7]
res_list = list(generate_running_sum(summands=list_a, index_list=ind_list))
print(res_list)
Upvotes: 2
Reputation: 3419
I did it with a combination of zip
and np.cumsum
.
I considered indexes from [0-4)
,[4-7)
and [7-(len(list)))
.
And then I found the cumulative sum of each slice of the list and then put it in another list:
import numpy as np
list_a = [100, 5, 1, 2, 200, 3, 1, 300, 6, 6]
ind_list = [0, 4, 7]
def createcumsum(list_a, ind_list):
# For adding the end of the list to the indexes if it doesn't exist
if ind_list[-1] != len(list_a):
ind_list.append(len(list_a))
res_list=[]
# Considers the indexes from 0-4, 4-7, 7-(endoflist)
for x,y in zip(ind_list, ind_list[1:]):
# Take cumulativesum on the above mentioned slices
res_list.extend(np.cumsum(list_a[x:y]))
return res_list
print(createcumsum(list_a, ind_list))
[100, 105, 106, 108, 200, 203, 204, 300, 306, 312]
Upvotes: 1
Reputation: 3382
Use the following:
cs= np.cumsum(list_a)
for i in ind_list:
if i==0:
continue
cs[i:]-=cs[i-1]
Result:
cs
>>array([100, 105, 106, 108, 200, 203, 204, 300, 306, 312])
Upvotes: 10
Reputation: 221614
It's NumPy tagged and finding vectorized solutions is fun, so here's one -
def intervaled_cumsum(list_a, ind_list):
a = np.array(list_a)
a[ind_list[1:]] -= np.add.reduceat(a,ind_list)[:-1]
return a.cumsum()
Sample run -
In [54]: list_a = [100, 5, 1, 2, 200, 3, 1, 300, 6, 6]
In [55]: ind_list = [0, 4, 7]
In [56]: intervaled_cumsum(list_a, ind_list)
Out[56]: array([100, 105, 106, 108, 200, 203, 204, 300, 306, 312])
Upvotes: 3
Reputation: 78750
Maximum itertools
abuse!
Setup
from itertools import accumulate, chain, islice, tee
def pairwise(iterable):
'pairwise recipe from itertools docs'
it1, it2 = tee(iterable)
next(it2)
return zip(it1, it2)
list_a = [100, 5, 1, 2, 200, 3, 1, 300, 6, 6]
ind_list = [0, 4, 7]
Solution
ind_list.append(None)
result = list(chain.from_iterable(accumulate(islice(list_a, start, stop))
for start, stop in pairwise(ind_list)))
print(result)
Output
[100, 105, 106, 108, 200, 203, 204, 300, 306, 312]
The idea behind using itertools
facilities whenever possible here is to avoid creating intermediary list-slices which unnecessarily consume memory.
~edit~~
Memory efficient Python 2.7 solution
from itertools import chain, islice, izip, tee
def pairwise(iterable):
'pairwise recipe from itertools docs'
it1, it2 = tee(iterable)
next(it2)
return izip(it1, it2)
def my_cumsum(iterable):
s = 0
for x in iterable:
s += x
yield s
list_a = [100, 5, 1, 2, 200, 3, 1, 300, 6, 6]
ind_list = [0, 4, 7]
ind_list.append(None)
result = list(chain.from_iterable(my_cumsum(islice(list_a, start, stop))
for start, stop in pairwise(ind_list)))
print(result)
Upvotes: 1
Reputation: 535
I had to expand the ind_list manually:
ind_list = [0, 4, 7] + [len(list_a)]
Just running a nested loop with the nested range set to the ind_list above:
count, final = 0, []
for i in range(len(ind_list)-1):
count = 0
for i in range(ind_list[i],ind_list[i+1]):
count += list_a[i]
final.append(count)
Upvotes: 1
Reputation: 73470
Some itertools
to the rescue:
from itertools import accumulate as acc, chain
list_a = [100, 5, 1, 2, 200, 3, 1, 300, 6, 6]
ind_list = [0, 4, 7]
list(chain(*(acc(list_a[x:y]) for x, y in zip(ind_list, ind_list[1:]+[None]))))
# [100, 105, 106, 108, 200, 203, 204, 300, 306, 312]
Upvotes: 4