inverting a dictionary in Python - python

This is a code that inverts a dictionary but I've faced some troubles in order to understand the role of each element of code in the defined function invert_dict(dic), it would be great if someone breaks it down to me and explains to me the mission of each element.
Thank you.
animals = {'Lion':["meet", 1.2 ,'yellow'],'Cat':["milk", 0.3,'white'],'dog':["Dog", 1,'black']}
def invert_dict(dic):
return {v: d.setdefault(v, []).append(k) or d[v] for d in ({},) for k in dic for v in dic[k]}
print(invert_dict(animals))
The output:
{'meet': ['Lion'], 1.2: ['Lion'], 'yellow': ['Lion'], 'milk': ['Cat'], 0.3: ['Cat'], 'white': ['Cat'], 'Dog': ['dog'], 1: ['dog'], 'black': ['dog']}

Sounds like a classic use for defaultdict
animals = {'Lion':["meet", 1.2 ,'yellow'],'Cat':["milk", 0.3,'white'],'dog':["Dog", 1,'black']}
from collections import defaultdict
def invert_dict(d):
ret = defaultdict(list)
d = [(v,k) for k, lst in d.items() for v in lst]
for k,v in d.items():
ret[k].append(v)
return dict(ret)

{v: d.setdefault(v, []).append(k) or d[v] for d in ({},) for k in dic for v in dic[k]}
Let's break this down from the right side
for k in dic for v in dic[k]
Uses the dictionary with value as lists { k: [v1, v2] } and extracts the corresponding key as k and list of values in v = [v1, v2]
v: d.setdefault(v, []).append(k) or d[v] for d in ({},)
The for v in dic[k] loops on the values for each key i.e. [v1, v2] and uses a defaultdict to identify the value already exists in the resulting dictionary, if it does, it appends the key from the original dictionary to the new resulting inverted dictionary. If not, it assigns the key.
For a sample { k: [v1, v2, v3], z: [v1] }, after each run, the state of the inverted dictionary is as follows:
{}
{ v1 : [k] }
{ v1 : [k], v2 : [k] }
{ v1 : [k], v2 : [k], v3: [k] }
{ v1 : [k, z], v2 : [k], v3: [k] } # because v1 already exists, z is appended
This is the equivalent of:
from collections import defaultdict
def invert_dict_exploded(dic):
d = defaultdict(list)
for key in dic:
for value in dic[key]:
d[value].append(key)
return dict(d)

We need to go in order, from the outside to the inside:
Braces and colon notation
For loops
Append or value
Braces
Braces are used in Python to create a dictionary or a set. In this case they are used to create a dictionary since we have the {key: value} notation. In this discussion let's call it "the returned dictionary".
For loops
These for loops are the equivalent of:
for d in ({},):
for k in dic:
for v in dic.values():
...
This is a shorthand notation that comes in handy to create collections quickly. It's normally quite readable when it's not, like in this case, abused.
The for d in ({},) is only used to declare a dictionary that will sit there while iterating through the keys and values (because as we see above, it's in the outer-most loop). The dictionary is named d.
Append or value
For each key/value pair, we create a new key in the returned dictionary by using the colon notation, where the key is actually one of the input values, and then we do two separate things:
Append an item in our d dictionary, which is always a list
Assign the list to the given key
The k.setdefault(v, []) bit will set a default value of [] if no key v is found in the dictionary and then will return that list (the newly created empty list or the list found at that key), which is then used by the .append(k) bit to append the key as a value to that list. This takes care of cases where you have items in your input list with the same value, collecting all the keys together for that value, like in:
animals = {'Lion':["meet", 1.2 ,'yellow'],'Cat':["milk", 0.3,'black'],'dog':["Dog", 1,'black']}
Where you can see the multiple lists containing the "black" item and will output the following:
{'meet': ['Lion'], 1.2: ['Lion'], 'yellow': ['Lion'], 'milk': ['Cat'], 0.3: ['Cat'], 'black': ['Cat', 'dog'], 'Dog': ['dog'], 1: ['dog']}
Notice both "Cat" and "dog" keys are added to the "black" list in the result.
Finally, the or part. The list.append() function always returns None since every function that doesn't return explicitly, automatically returns None in Python.
The or operator is used to short-circuit the expression. It's written as A or B and is to be read as "If A evaluates to a true value, the expression evaluates to A; if A evaluates to a false value, the expression evaluates to B". None always evaluates to false in boolean terms, so the expression d.setdefault(v, []).append(k) or d[v] always evaluates to d[v] but only after executing the setdefault() and append().
v: d.setdefault(v, []).append(k) or d[v] can therefore be read as:
Create a key v in our returned dictionary; if v is not a key of d, set d[v] = []; append to d[v] the value k and set d[v] as the value of v.

Related

Adding additional logic to a lambda function to get the minimum value from a python dictionary

I have a dictionary with values mapping some object to an integer; e.g. {node: 1, node: 2}.
I want to get the key that corresponds to the minimum value in this dictionary, which I know I can do with:
min_key = min(my_dict, key=lambda k: my_dict[k])
However, I'd also like to add the constraint that the key is not contained within some other set. Here's the pseudo-code that I wished worked:
min_key = min(my_dict, key=lambda k: my_dict[k] where k not in my_set)
Is there a way to write this as a one-liner in python as part of a lambda, or do I now have to explicitly loop through the dictionary and add the logic within the loop like this?
min_key, min_value = None, float('inf')
for k,v in my_dict.items():
if v < min_value and k not in my_set:
min_key = k
return min_key
Replace my_dict with a dictionary comprehension that returns the filtered dictionary.
min_key = min({k:v for k, v in my_dict.items() if k not in my_set},
key = lambda k: my_dict[k])
Just take the minimum over the filtered keys instead of all keys:
min_key = min(my_dict.keys() - my_set, key=my_dict.get)
(Note I also replaced your key function, no need to write your own.)
It's similar to #Barmar's answer but you can also use set.difference between my_dict and my_set to filter the relevant dictionary:
out = min(set(my_dict).difference(my_set), key = lambda k: my_dict[k])

Iterate through list of dictionories and compare with other dictionories

I have the following dictionaries
dic1 = { 'T1': "HI , china" , 'T2': "HI , finland" ,'T3': "HI , germany"}
dic2 = { 'T1': ['INC1','INC2','INC3'] , 'T2': ['INC2','INC4','INC5'],'T3': ['INC2','INC5']}
dic3 = { 'INC1': {'location':'china'} , 'INC2': {'location':'germany'},'INC3': {'location':'germany'},'INC4': {'location':'finland'}}
I need to remove the values in dic2 based on the dic1,dic3
example :
I have to iterate through dic2 first and check the T1 and its INC values.
If the corresponding T1 key value in the Dic1 matches with the corresponding INC values in the dic3 the keep the value in dic2 otherwise pop the value.
Detaileded explanation given in the picture. And I am expecting the following output.
dic2 = { 'T1': ['INC1'] , 'T2': ['INC4'],'T3': ['INC2']}
example code :
for k, v in dic2.items():
for k1, v1 in dic1.items():
if k is k1:
print k
for k2 in v:
for k3 in dic3.items():
I am new to python. I have tried the above pieces of code and I got struck down.Could you please help me out.
Can be done in a one-liner:
>>> {k: [i for i in v if dic3.get(i, {}).get('location', '#') in dic1[k]] for k, v in dic2.items()}
{'T1': ['INC1'], 'T2': ['INC4'], 'T3': ['INC2']}
[i for i in v if dic3.get(i, {}).get('location', '#') is the list comprehension to pick only the values from dic2's lists if dic3[i]['location'] is inside dic1[k], where i is the key in dict d3 and k is the key from dict d2 as well as dict d1.
I used dic3.get(i, {}).get('location', '#') (rather than dic3[i]['location'], you get KeyError for key INC5 which is not in dic3) to avoid the issue of key i not being in dic3 (would return an empty dict for my next .get) and in the second .get, again I use it to return the location key's corresponding value if it exists and check if that returned string lies inside the values of dict d1.
I return # (can be any string/char) if I know that the key i doesn’t exist in dic3 (i not existing will essentially give {}.get('location', '#') which in turn will give #, this is sure to fail the membership in d1, hence it's Ok).
Toned down for-loop version:
>>> ans = {}
>>> for k, v in dic2.items():
... temp = []
... for i in v:
... if i in dic3 and dic3[i]['location'] in dic1[k]:
... temp.append(i)
... ans[k] = temp
...
>>> ans
{'T1': ['INC1'], 'T2': ['INC4'], 'T3': ['INC2']}
From my understanding, you are supposed to modify the original dic2 dictionary rather than creating a new one for your answer, this is what I got:
delete = []
for key, value in dic2.items():
loc = dic1[key][5:]
for inc in value:
if inc not in dic3:
delete.append(inc)
else:
if loc != dic3.get(inc)['location']:
delete.append(inc)
for item in delete:
value.remove(item)
delete = []
print(dic2)
>>> {'T1': ['INC1'], 'T2': ['INC4'], 'T3': ['INC2']}
The first for loop iterates through dic2 and sets the location required to the variable loc. The next one iterates through the lists (values) and adds them to a delete list.
At the end of each loop, it iterates through the delete list, removing each element from the value list, and then setting delete to an empty list.
I am also relatively new to python so I am sure there could be efficiency issues.
This is essentially the same as the other answers but as a one-liner, it may be more readable (maybe not, since it's subjective).
{k: [el] for k, v in dic2.items() for el in v if (el in dic3.keys() and dic3[el]['location'] in dic1[k])}

inversing a dictionary in python with duplicate values

I need to inverse a dictionary so that each old value will now be a key and the old keys will be the new values.
The trick is that there could be multiple values that are the same in the old dictionary so I need each value in the new dictionary to be a list, and if there were identical values in the old dictionary then they both will be in the list of the value of the new dictionary.
for example:
the dictionary {"python" : 1, "is" : 1, "cool" : 2}
would end up as: {1 : ["python", "is"], 2 : ["cool"]}
this is what I tried:
def inverse_dict(my_dict):
new_dict = {}
values_list = list(my_dict.values())
new_dict = new_dict.fromkeys(values_list)
for key in new_dict:
new_dict[key] = []
for old_key in my_dict:
new_dict[my_dict[old_key]] = list(new_dict[my_dict[old_key]]).append(old_key)
return new_dict
Would greatly appreciate any help with my approach (and better approaches to the problem) as I am very new to Python, thanks!
You can use dict.setdefault check if a key exists in the dictionary and if not, create new value (in this case empty list []):
d = {"python" : 1, "is" : 1, "cool" : 2}
reversed_d = {}
for k, v in d.items():
reversed_d.setdefault(v, []).append(k)
print(reversed_d)
Prints:
{1: ['python', 'is'], 2: ['cool']}
This can be more explicitly rewritten as:
d = {"python" : 1, "is" : 1, "cool" : 2}
reversed_d = {}
for k, v in d.items():
if v not in reversed_d:
reversed_d[v] = [k]
else:
reversed_d[v].append(k)
print(reversed_d)
You can use a defaultdict to avoid the pre-fill step
from collections import defaultdict
def inverse_dict(my_dict: dict):
new_dict = defaultdict(list)
for k, v in my_dict.items():
new_dict[v].append(k)
return new_dict
Though I prefer #azro's answer with the default dict, another solution is doing it with dictionary and list comprehensions.
It looks like this:
{value : [key for key in my_dict if my_dict[key] == value] for value in set(my_dict.values())}
What it does is runs over the values of the dictionary without duplicates - set(my_dict.values()).
It builds every value as a key (because it's on the left side of the ":").
And its value is a list of the keys that point to that value - [key for key in my_dict if my_dict[key] == value].

Check if value of one dictionary is the key for the other

I basically want to check if the value in one dictionary is the key in the other.
So for example, I have two dictionaries
a = {armani: jeans, dolce: gabbana}
b = {jeans: robo, jobs: versace}
I wrote code to do the check such that it only obtains the values that is the key in the other dictionary. So in this case, I just want to display {armani: robo} as jeans was already in both. So like the value of jeans in the second dictionary then becomes the main value in the new final dictionary
This is the code:
{k:v for k,v in a.items() if v in b.items()}
But it doesn't work and I don't know how to do the check to see if the value is the key in another list
This should work:
{k:b[v] for k,v in a.items() if v in b}
You were just missing two elements:
You don't need to write if v in b.items() because Python interprets if v in b as "if v is in the keys of b".
You need to map k not to v itself, but to v's value in b, which is b[v].
Alternatively, you could filter with set intersection.
a = {'armani': 'jeans', 'dolce': 'gabbana'}
b = {'jeans': 'robo', 'jobs': 'versace'}
c = set(a.values()).intersection(b)
d = {k:b[k] for k in c}
# or as a one-liner
e = {k:b[k] for k in set(a.values()).intersection(b)}
It might be faster than looping through the whole dictionary.
I think you need:
a = {"armani": "jeans", "dolce": "gabbana"}
b = {"jeans": "robo", "jobs": "versace"}
res = {k1:v2 for k1,v1 in a.items() for k2,v2 in b.items() if v1 ==k2}
print(res)
Output:
{'armani': 'robo'}

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.

Categories