Python partial sum of dictionary values

Viewed 100

I have a dictionary dgr of n (for this example let's just say n=10) objects named 'gr0' up to 'gr(n-1)' (in case n=10 up to 'gr9') with float values ordered to them in the following fashion:

dgr = {'gr0': 1.3, 'gr1': 1.2, 'gr2': 1.9, 'gr3': 2.4, 'gr4': 2.2, 'gr5': 4.2, 'gr6': 1.2, 'gr7': 5.5, 'gr8': 4.3, 'gr9': 3.2}

I have another dictionary

dy = {}

which is supposed to be filled as such (the 2nd line produces the to-be-fixed error):

for x in range(0,n):
    dy["y{0}".format(x)] = sum(dgr.values()[0,x])

so 'y0' is 1.3, but 'y3' is 6.8 so the sum of the values of 'gr0' up to 'gr3'. Summing all values of gr{x}' (so leaving [0,x] out) works but if I try to do the partial sums with [0,x] it gives me this error:

'dict_values' object is not subscriptable
6 Answers

This might help. I have added n's value to 10(because you start from 0) in the loop you can let it be n though):

for x in range(0,10): # Note: Here n = 10
    dy["y{0}".format(x)] = sum(list(dgr.values())[0:x+1])

Output:

{'y0': 1.3, 'y1': 2.5, 'y2': 4.4, 'y3': 6.800000000000001, 'y4': 9.0,
'y5': 13.2, 'y6': 14.399999999999999, 'y7': 19.9, 'y8': 24.2, 'y9': 27.4}

What you were doing wrong?

You cannot subscript the dictionary values directly. Instead, try to get the values, convert them to a list, and then use slicing. In slicing, you need to use 0:x+1 otherwise y0 will be 0 and all following keys will have sum till x i.e. 1 key before than intended output. Also, use : (colon) for slicing and not , (commas).

A solution accomplishes what you are actually trying to do:

for x in range(0,n):
    dy["y{0}".format(x1)] = sum([dgr["gr{0}".format(x2)] for x2 in range(0, x+1)])

You can do

dy["y{0}".format(x)] = sum(i for idx, i in enumerate(dgr.values()) if idx <= x)

or even avoid dgr.values() altogether

dy["y{0}".format(x)] = sum(dgr[k] for idx, k in enumerate(dgr) if idx <= x)

or more efficiently (for this input)

dy["y{0}".format(x)] = sum(tuple(dgr.values())[0:x + 1])

Output from printing dy

{'y0': 1.3,
 'y1': 2.5,
 'y2': 4.4,
 'y3': 6.800000000000001,
 'y4': 9.0,
 'y5': 13.2,
 'y6': 14.399999999999999,
 'y7': 19.9,
 'y8': 24.2,
 'y9': 27.4}

Timings using %%timeit -n 1000000.

# tuple 
5.03 µs ± 13.5 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)

# list
5.45 µs ± 64.9 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)

# enumerate (dgr.values())
10 µs ± 40.2 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)

# enumerate (no dgr.values())
10.8 µs ± 69.7 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)

# lancelot 
3.52 µs ± 47.6 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)

# arnie 
3.01 µs ± 84.1 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)

Slice syntax uses a comma to separate the start, end and step values. Therefore:

dgr = {'gr0': 1.3, 'gr1': 1.2, 'gr2': 1.9, 'gr3': 2.4, 'gr4': 2.2, 'gr5': 4.2, 'gr6': 1.2, 'gr7': 5.5, 'gr8': 4.3, 'gr9': 3.2}

vlist = list(dgr.values())

dy = {f'y{y}': sum(vlist[:y+1]) for y in range(len(vlist))}

print(dy)

...should give you what I think you're trying to achieve namely:

{'y0': 1.3, 'y1': 2.5, 'y2': 4.4, 'y3': 6.800000000000001, 'y4': 9.0, 'y5': 13.2, 'y6': 14.399999999999999, 'y7': 19.9, 'y8': 24.2, 'y9': 27.4}

Since you are looping over the key numbers anyway, a straightforward and efficient approach is to keep track of the partial (cumulative) sum along the way:

n = 10
dgr = {'gr0': 1.3, 'gr1': 1.2, 'gr2': 1.9, 'gr3': 2.4, 'gr4': 2.2, 
       'gr5': 4.2, 'gr6': 1.2, 'gr7': 5.5, 'gr8': 4.3, 'gr9': 3.2}

dy = {}
cumsum = 0

for i in range(n):
    cumsum += dgr[f"gr{i}"]
    dy[f"y{i}"] = cumsum
    
print(dy)
{'y0': 1.3, 'y1': 2.5, 'y2': 4.4, 'y3': 6.800000000000001, 'y4': 9.0,
 'y5': 13.2, 'y6': 14.399999999999999, 'y7': 19.9, 'y8': 24.2, 'y9': 27.4}

dictionary is not subscriptable because it is not work like an array object. set, list and tuple subscriple elements so you need values of dictionary sample solution like that

dgr={'gr0': 1.3, 'gr2': 1.9, 'gr3': 2.4, 'gr1': 1.2,   'gr4': 2.2, 'gr5': 4.2, 'gr6': 1.2, 'gr7': 5.5, 'gr8': 4.3, 'gr9': 3.2}
dgr = sorted(dgr.items())
grs = [gr[1] for gr in dgr]
dy = {}
for i in range(len(grs)):
  dy[f'y{i}'] = sum(grs[0:i+1])
Related