Subtracting elements from dictionaries

Question:

I have two dictionaries, let’s say:

dict_a = {"A": 3.0, "C": 4.0, "B": 6.0}
dict_b = {"A": 3.0, "C": 5.0, "D": 1.0}

and I want to calculate a subtraction dict_a - dict_b so the output gives me:

{"A": 0.0, "B": 6.0, "C": -1.0, "D": -1.0}

Googling for some time I have seen people using Counter to perform operations like this, but doing something simple like

dict(Counter(dict_a) - Counter(dict_b))

only gives me

{"A": 0.0, "B": 6.0}

i.e., if the key is not in dict_a, it won’t appear in the output.

So far, I have managed the following solution:

dict_b2 = dict_b.copy()
dict_c = {}
for i in dict_a.keys():
   dict_c.update({i: dict_a[i] - dict_b2.pop(i,0.0)})
   # Changes sign to the remaining values from dict_b2
dict_c.update({k: 0.0 - v for k, v in dict_b2.iteritems()})

but I know there has to be a much more elegant and efficient way of doing it.

Any ideas?

Asked By: eSedano

||

Answers:

You can try using a dict comprehension:

new_dict = {k:dict_a.get(k, 0) - dict_b.get(k, 0) 
            for k in set(dict_a).union(set(dict_b))}

# {'A': 0.0, 'C': -1.0, 'B': 6.0, 'D': -1.0}

Here we are using dict#get(key, default) to avoid getting a KeyError (when the key doesn’t belong to one of the dictionaries).

Answered By: Christian Tapia

It turns out that Counter has a (not very well documented) subtract method:

>>> dict_a = {"A": 3.0, "C": 4.0, "B": 6.0}
>>> dict_b = {"A": 3.0, "C": 5.0, "D": 1.0}
>>> from collections import Counter
>>> a = Counter(dict_a)
>>> a.subtract(dict_b)
>>> a
Counter({'B': 6.0, 'A': 0.0, 'C': -1.0, 'D': -1.0})

Note that this doesn’t suffer from the “no values below 0” constraint which is imposed on Counter.__sub__, but it does do the operation in-place.

One thing that is a little cool is that I didn’t have to convert both dicts to a Counter — only the one on the left side of the operation.

Answered By: mgilson
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.