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]
We declare an empty list cum_list to which we will append elements to form the cumulative sum list. Initialize a sum variable sm=0. Start iterating over the input list, with each iteration we increment the sum value to previous value+ the current element. On each iteration, the sum value is appended to the cum_list.
Cumulative sums, or running totals, are used to display the total sum of data as it grows with time (or any other series or progression). This lets you view the total contribution so far of a given measure against time.
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])
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]
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])
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)
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)
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)
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]
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]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With