usernumber
usernumber

Reputation: 2186

Sum of values in list of dictionaries

I want to get the sum values for each key in all dictionaries of a list, and if a key is not present in one of the dictionaries, then its value is considered 0.

Suppose I have two dictionaries as such:

d1 = {'a' : 2, 'b' : 1, 'c' : 1}
d2 = {'a' : 3, 'b' : 1.1, 'd' : 2}
mylist = [d1, d2]

and I would like to define a sum function such that

>>> sum(mylist)
{'a' : 5, 'b' : 2.1, 'c' : 1, 'd' : 2}

If I only have two dictionaries, I can do

>>> for key, value in d2.items():
...    try:
...        d1[key] -= value
...    except KeyError: #if the key isn't in d1
...        d1[key] = -value

>>> d1
{'a' : 5, 'b' : 2.1, 'c' : 1, 'd' : 2}

But this is not extendable to an arbitrary number of dictionaries.

I also tried

>>> {k: sum(e[k] for e in mylist) for k in mylist[0]}
{'a' : 5, 'b' : 2.1, 'c' : 1}

But this doesn't give me the sum for elements that aren't in the first list (I'm missing the sum for 'd' in my example).

I could create a dictionary with all of the possible keys and add it to the front of my list

>>> d0 = {'a' : 0, 'b' : 0, 'c' : 0, 'd' : 0}
>>> newlist = [d0, d1, d2]
>>> {k: sum(e[k] for e in newlist) for k in newlist[0]}
{'a' : 5, 'b' : 2.1, 'c' : 1, 'd' : 2}

But creating d0 will be tedious.

I could also use Counter from collections

>>> counterlist = [Counter(d) for d in mylist]
>>> result = Counter()
>>> for c in counterlist:
...    result.update(c)
>>> dict(result)

But I'm not too happy about switching back and forth to Counter.

Or, I could implement an 'update-like' function

>>> def add(e, f):
...    for key, value in f.items():
...        try:
...            e[key] -= value
...        except KeyError:
...            e[key] = -value

>>> result = dict()
>>> for d in mylist:
...    add(result, d)
>>> result
{'a' : 5, 'b' : 2.1, 'c' : 1, 'd' : 2}

But this makes me feel like I'm reinventing the wheel.

Is there a more pythonic way of doing this?

Upvotes: 2

Views: 2490

Answers (3)

usernumber
usernumber

Reputation: 2186

Based on Christian Stade-Schuldt's answer and Jon Clements' answer, here is what I came up with

all_keys = set().union(*mylist)
{k: sum(e[k] for e in mylist) for k in allkeys}

Upvotes: 1

Christian Stade-Schuldt
Christian Stade-Schuldt

Reputation: 4861

First get all keys and set up a new dictionary from your list of dictionaries:

d1 = {'a' : 2, 'b' : 1, 'c' : 1}
d2 = {'a' : 3, 'b' : 1.1, 'd' : 2}
mylist = [d1, d2]
sum_dict = dict.fromkeys(set().union(*mylist), 0)

After that that is simple to just iterate over the list of dictionaries and the keys:

for d in mylist:
    for k in d.keys():
        sum_dict[k] += d[k]

Upvotes: 2

Mp0int
Mp0int

Reputation: 18727

Simple Counter solution:

d1 = {'a' : 2, 'b' : 1, 'c' : 1}
d2 = {'a' : 3, 'b' : 1.1, 'd' : 2}
d3 = ...
...
mylist = [d1, d2, d3 , ...]

cnt = Counter()
for _c in mylist:
    cnr += Counter(_c)
print cnr
>> Counter({'a': 5, 'b': 2.1, 'd': 2, 'c': 1})
dict(cnr)
>> {'a': 5, 'b': 2.1, 'd': 2, 'c': 1}

For non-counter solution, you can use dict.get:

sums = {}
for _dics in mylist:
    for key, val in _dics.items():
        sums[key] = sums.get(key, 0) + val

Logic is simple, if key do not exists in sums dict, then get it as 0

Upvotes: 1

Related Questions