Inverting Dictionaries in Python - python

I want to know which would be an efficient method to invert dictionaries in python. I also want to get rid of duplicate values by comparing the keys and choosing the larger over the smaller assuming they can be compared. Here is inverting a dictionary:
inverted = dict([[v,k] for k,v in d.items()])

To remove duplicates by using the largest key, sort your dictionary iterator by value. The call to dict will use the last key inserted:
import operator
inverted = dict((v,k) for k,v in sorted(d.iteritems(), key=operator.itemgetter(1)))

Here is a simple and direct implementation of inverting a dictionary and keeping the larger of any duplicate values:
inverted = {}
for k, v in d.iteritems():
if v in inverted:
inverted[v] = max(inverted[v], k)
else:
inverted[v] = k
This can be tightened-up a bit with dict.get():
inverted = {}
for k, v in d.iteritems():
inverted[v] = max(inverted.get(v, k), k)
This code makes fewer comparisons and uses less memory than an approach using sorted().

Related

Splitting a dictionary by key suffixes

I have a dictionary like so
d = {"key_a":1, "anotherkey_a":2, "key_b":3, "anotherkey_b":4}
So the values and key names are not important here. The key (no pun intended) thing, is that related keys share the same suffix in my example above that is _a and _b.
These suffixes are not known before hand (they are not always _a and _b for example, and there are an unknown number of different suffixes.
What I would like to do, is to extract out related keys into their own dictionaries, and have all generated dictionaries in a list.
The output from above would be
output = [{"key_a":1, "anotherkey_a":2},{"key_b":3, "anotherkey_b":4}]
My current approach is to first get all the suffixes, and then generate the sub-dicts one at a time and append to the new list
output = list()
# Generate a set of suffixes
suffixes = set([k.split("_")[-1] for k in d.keys()])
# Create the subdict and append to output
for suffix in suffixes:
output.append({k:v for k,v in d.items() if k.endswith(suffix)})
This works (and is not prohibitively slow or anyhting) but I am simply wondering if there is a more elegant way to do it with a list or dict comprehension? Just out of interest...
Make your output a defaultdict rather than a list, with suffixes as keys:
from collections import defaultdict
output = defaultdict(lambda: {})
for k, v in d.items():
prefix, suffix = k.rsplit('_', 1)
output[suffix][k] = v
This will split your dict in a single pass and result in something like:
output = {"a" : {"key_a":1, "anotherkey_a":2}, "b": {"key_b":3, "anotherkey_b":4}}
and if you insist on converting it to a list, you can simply use:
output = list(output.values())
You could condense the lines
output = list()
for suffix in suffixes:
output.append({k:v for k,v in d.items() if k.endswith(suffix)})
to a list comprehension, like this
[{k:v for k,v in d.items() if k.endswith(suffix)} for suffix in suffixes]
Whether it is more elegant is probably in the eyes of the beholder.
The approach suggested by #Błotosmętek will probably be faster though, given a large dictionary, since it results in less looping.
def sub_dictionary_by_suffix(dictionary, suffix):
sub_dictionary = {k: v for k, v in dictionary.items() if k.endswith(suffix)}
return sub_dictionary
I hope it helps

Efficiently filtering a dictionary in-place

We have a dictionary d1 and a condition cond. We want d1 to contain only the values that satisfy the condition cond. One way to do it is:
d1 = {k:v for k,v in d1.items() if cond(v)}
But, this creates a new dictionary, which may be very memory-inefficient if d1 is large.
Another option is:
for k,v in d1.items():
if not cond(v):
d1.pop(k)
But, this modifies the dictionary while it is iterated upon, and generates an error: "RuntimeError: dictionary changed size during iteration".
What is the correct way in Python 3 to filter a dictionary in-place?
If there are not many keys the corresponding values of which satisfy the condition, then you might first aggregate the keys and then prune the dictionary:
for k in [k for k,v in d1.items() if cond(v)]:
del d1[k]
In case the list [k for k,v in d1.items() if cond(v)] would be too large, one might process the dictionary "in turns", i.e., to assemble the keys until their count does not exceed a threshold, prune the dictionary, and repeat until there are no more keys satisfying the condition:
from itertools import islice
def prune(d, cond, chunk_size = 1000):
change = True
while change:
change = False
keys = list(islice((k for k,v in d.items() if cond(v)), chunk_size))
for k in keys:
change = True
del d[k]

short for python dictionary

I tried a few searches but I didn't really know how to ask. I understand short form for loops but this portion within a dictionary is confusing me.
resistances = {k: v if random.random() > self.mutProb else
not v for k, v in self.resistances.items()}
Is it setting k as the key initially and then cycling through it later on? I'm having difficulty imagining what the 'long hand' would be.
You have a dictionary comprehension, and for each iteration of the for loop, two expressions are executed. One for the key, and one for the value.
So in the expression:
{k: v if random.random() > self.mutProb else not v
for k, v in self.resistances.items()}
both k and v if random.random() > self.mutProb else not v are expressions, and the first produces the key, the second the value of each key-value pair for the resulting dictionary.
If you were to use a for loop, the above would be implemented as:
resistances = {}
for k, v in self.resistances.items():
key = k
value = v if random.random() > self.mutProb else not v
resistances[key] = value
In your example, the key is simply set to the value of the variable k, but you can use more complex expressions too.
Dictionary comprehensions are a specialisation of Dictionary Displays; the other form produces a dictionary without looping, from a static list of key-value pairs, and is perhaps more familiar to you:
d = {key1: value1, key2: value2}
but the documentation states:
A dict comprehension, in contrast to list and set comprehensions, needs two expressions separated with a colon followed by the usual “for” and “if” clauses. When the comprehension is run, the resulting key and value elements are inserted in the new dictionary in the order they are produced.

Comparing dictionaries on shared keys only

I have two dictionaries with some shared keys, and some different ones. (Each dictionary has some keys not present in the other). What's a nice way to compare the two dictionaries for equality, as if only the shared keys were present?
In other words I want a simplest way to calculate the following:
commonkeys = set(dict1).intersection(dict2)
simple1 = dict((k, v) for k,v in dict1.items() if k in commonkeys)
simple2 = dict((k, v) for k,v in dict2.items() if k in commonkeys)
return simple1 == simple2
I've managed to simplify it to this:
commonkeys = set(dict1).intersection(dict2)
return all(dict1[key] == dict2[key] for key in commonkeys)
But I'm hoping for an approach that doesn't require precalculation of the common keys. (In reality I have two lists of dictionaries that I'll be comparing pairwise. All dictionaries in each list have the same set of keys, so if a computation like commonkeys above is necessary, it would only need to be done once.)
What about the following?
return all(dict2[key] == val for key, val in dict1.iteritems() if key in dict2)
Or even shorter (although it possibly involves a few more comparisons):
return all(dict2.get(key, val) == val for key, val in dict1.iteritems())
Try this
dict((k, dict1[k]) for k in dict1.keys() + dict2.keys() if dict1.get(k) == dict2.get(k))
O(m + n) comparisons.
If you want a true/false result put a simple check on the above result. If not none return true

Returning unique elements from values in a dictionary

I have a dictionary like this :
d = {'v03':["elem_A","elem_B","elem_C"],'v02':["elem_A","elem_D","elem_C"],'v01':["elem_A","elem_E"]}
How would you return a new dictionary with the elements that are not contained in the key of the highest value ?
In this case :
d2 = {'v02':['elem_D'],'v01':["elem_E"]}
Thank you,
I prefer to do differences with the builtin data type designed for it: sets.
It is also preferable to write loops rather than elaborate comprehensions. One-liners are clever, but understandable code that you can return to and understand is even better.
d = {'v03':["elem_A","elem_B","elem_C"],'v02':["elem_A","elem_D","elem_C"],'v01':["elem_A","elem_E"]}
last = None
d2 = {}
for key in sorted(d.keys()):
if last:
if set(d[last]) - set(d[key]):
d2[last] = sorted(set(d[last]) - set(d[key]))
last = key
print d2
{'v01': ['elem_E'], 'v02': ['elem_D']}
from collections import defaultdict
myNewDict = defaultdict(list)
all_keys = d.keys()
all_keys.sort()
max_value = all_keys[-1]
for key in d:
if key != max_value:
for value in d[key]:
if value not in d[max_value]:
myNewDict[key].append(value)
You can get fancier with set operations by taking the set difference between the values in d[max_value] and each of the other keys but first I think you should get comfortable working with dictionaries and lists.
defaultdict(<type 'list'>, {'v01': ['elem_E'], 'v02': ['elem_D']})
one reason not to use sets is that the solution does not generalize enough because sets can only have hashable objects. If your values are lists of lists the members (sublists) are not hashable so you can't use a set operation
Depending on your python version, you may be able to get this done with only one line, using dict comprehension:
>>> d2 = {k:[v for v in values if not v in d.get(max(d.keys()))] for k, values in d.items()}
>>> d2
{'v01': ['elem_E'], 'v02': ['elem_D'], 'v03': []}
This puts together a copy of dict d with containing lists being stripped off all items stored at the max key. The resulting dict looks more or less like what you are going for.
If you don't want the empty list at key v03, wrap the result itself in another dict:
>>> {k:v for k,v in d2.items() if len(v) > 0}
{'v01': ['elem_E'], 'v02': ['elem_D']}
EDIT:
In case your original dict has a very large keyset [or said operation is required frequently], you might also want to substitute the expression d.get(max(d.keys())) by some previously assigned list variable for performance [but I ain't sure if it doesn't in fact get pre-computed anyway]. This speeds up the whole thing by almost 100%. The following runs 100,000 times in 1.5 secs on my machine, whereas the unsubstituted expression takes more than 3 seconds.
>>> bl = d.get(max(d.keys()))
>>> d2 = {k:v for k,v in {k:[v for v in values if not v in bl] for k, values in d.items()}.items() if len(v) > 0}

Categories