How to join two dictionaries with overlapping key values in python? - python

For example if my dictionaries are,
dict1 = {1:'a',2:'b',3:'c'}
dict2 = {1:'d',2:'e'}
Then I want the joined dictionary to be,
{1:'a',2:'b',3:'c',4:'d',5:'e'}

Looks like you don't care about the keys at all. So just figure out what order you want to join the dictionaries in and do:
import itertools
ordered_dicts = [dict1, dict2] # N.B. normal dicts, not OrderedDicts!
ordered_values = [v for d in itertools.chain(ordered_dicts) for _, v in sorted(d.items())]
result = dict(zip(itertools.count(1), ordered_values))

Try this:
dict1 = {1:'a', 2:'b', 3:'c'}
dict2 = {1:'d', 2:'e'}
# Change the name of a key
# Note this will delete the keys 1 and 2 in dict2
dict2[4] = dict2.pop(1) # Return the corresponding value
dict2[5] = dict2.pop(2)
# Merging the two dicts (works in Python 3.5+)
result = {**dict1, **dict2}
>>> result
{1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e'}
Alternatively, if you want to keep dict2 unchanged:
dict1 = {1:'a', 2:'b', 3:'c'}
dict2 = {1:'d', 2:'e'}
for i, k in enumerate(dict2, start=len(dict1) + 1):
dict1[i] = dict2[k]
>>> dict1
{1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e'}

We can use Pandas:
import pandas as pd
dict1 = {1:'a',2:'b',3:'c'}
dict2 = {1:'d',2:'e'}
df1 = pd.DataFrame.from_dict(dict1, orient='index')
df2 = pd.DataFrame.from_dict(dict2, orient='index')
pd.concat([df1,df2]).set_axis(range(1,len(df1)+len(df2)+1), inplace=False).to_dict()[0]
Output:
{1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e'}

enumerate has a start keyword argument, you can count from whatever number you want. Here you can use it to solve your issue without importing any heavy external module or many loops:
dict1 = {1:'a',2:'b',3:'c'}
dict2 = {1:'d',2:'e'}
for index,values in enumerate(dict2.items(),start=len(dict1)+1):
dict1[index] = values[1]
print(dict1)
output:
{1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e'}

dict1 = {1:'a',2:'b',3:'c'}
dict2 = {1:'d',2:'e'}
d = {}
# creating a new dictionary same as dict1
for k,v in dict1.items():
d[k] = v
# if dict1 needs to be updated then
# use dict1 instead of d below
for k,v in dict2.items():
if k in d:
d[max(d)+1] = v # considering the max key in dict1, increment by 1
# else:
# any other condition
Output
# d: {1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e'}

Related

How to efficiently remove elements from dicts that have certain value patterns?

For example, in dict1 the keys 1, 2, 3 all have the same value 'a', but the keys 3 and 5 have different values, 'b' and 'd'. What I want is:
If N keys have the same value and N >=3, then I want to remove all other elements from the dict and only keep those N key values, which means 'b' & 'd' have to be removed from the dict.
The following code works, but it seems very verbose. Is there a better way to do this?
from collections import defaultdict
dict1 = {1:'a', 2:'a', '3':'b', '4': 'a', '5':'d'}
l1 = [1, 2, 3, 4, 5]
dict2 = defaultdict(list)
for k, v in dict1.items():
dict2[v].append(k)
to_be_removed = []
is_to_be_removed = False
for k, values in dict2.items():
majority = len(values)
if majority>=3:
is_to_be_removed = True
else:
to_be_removed.extend(values)
if is_to_be_removed:
for d in to_be_removed:
del dict1[d]
print(f'New dict: {dict1}')
You can use collections.Counter to get the frequency of every value, then use a dictionary comprehension to retain only the keys that have the desired corresponding value:
from collections import Counter
dict1 = {1:'a', 2:'a', '3':'b', '4': 'a', '5':'d'}
ctr = Counter(dict1.values())
result = {key: value for key, value in dict1.items() if ctr[value] >= 3}
print(result)
This outputs:
{1: 'a', 2: 'a', '4': 'a'}

Dictionary Comprehension in Python for key:[1,2,3] [duplicate]

This question already has answers here:
is it possible to reverse a dictionary in python using dictionary comprehension
(5 answers)
Closed 2 years ago.
While I've been improving my Python skills I have one question.
My code is below:
# def invertDictionary(dict):
# new_dict = {}
# for key, value in dict.items():
# if value in new_dict:
# new_dict[value].append(key)
# else:
# new_dict[value]=[key]
# return new_dict
def invertDictionary(dict):
new_dict = {value:([key] if value else [key]) for key, value in dict.items()}
return new_dict;
invertDictionary({'a':3, 'b':3, 'c':3})
I am trying to get output like {3:['a','b','c']}. I have achieved that using a normal for-loop; I just want to know how to get these results using a Dictionary Comprehension. I tried but in append it's getting an error. Please let me know how to achieve this.
Thanks in Advance!
You missed that you also need a list comprehension to build the list.
Iterate over the values in the dict, and build the needed list of keys for each one.
Note that this is a quadratic process, whereas the canonical (and more readable) for loop is linear.
d = {'a':3, 'b':3, 'c':3, 'e':4, 'f':4, 'g':0}
inv_dict = {v: [key for key, val in d.items() if val == v]
for v in set(d.values())}
result:
{0: ['g'],
3: ['a', 'b', 'c'],
4: ['e', 'f']
}
Will this do?
while your original version with a regular for loop is the best solution for this, here is a variation on #Prune answer that doesn't goes over the dict multiple times
>>> import itertools
>>> d = {'a':3, 'b':3, 'c':3, 'e':4, 'f':4, 'g':0}
>>> {group_key:[k for k,_ in dict_items]
for group_key,dict_items in itertools.groupby(
sorted(d.items(),key=lambda x:x[-1]),
key=lambda x:x[-1]
)
}
{0: ['g'], 3: ['a', 'b', 'c'], 4: ['e', 'f']}
>>>
first we sorted the items of the dict by value with a key function to sorted using a lambda function to extract the value part of the item tuple, then we use the groupby to group those with the same value together with the same key function and finally with a list comprehension extract just the key
--
as noted by Kelly, we can use the get method from the dict to get the value to make it shorter and use the fact that iteration over a dict give you its keys
>>> {k: list(g) for k, g in itertools.groupby(sorted(d, key=d.get), d.get)}
{0: ['g'], 3: ['a', 'b', 'c'], 4: ['e', 'f']}
>>>
You could use a defalutdict and the append method.
from collections import defaultdict
dict1 = {'a': 3, 'b': 3, 'c': 3}
dict2 = defaultdict(list)
{dict2[v].append(k) for k, v in dict1.items()}
dict2
>>> defaultdict(list, {3: ['a', 'b', 'c']})

Find top counted element in a dictionary of a dictionary

I have a dictionary of dictionary myDict:
myDict = { 'cow': {'A':2, 'B':5, 'C':3},
'cat': {'A':7, 'B':1, 'C':6},
'dog': {'A':1, 'B':4, 'C':3},
'bird': {'A':5, 'B':7, 'C':9}
}
I want to find the top count element for each animal in this case, i.e. the output should be:
myNewTable = {'cow':'B', 'cat':'A', 'dog':'B', 'bird':'C'}
I tried to achieve this using the following code:
myNewTable = max(x.items(), key=operator.itemgetter(1))[0] for x in myDict
but got the following error:
File "<ipython-input-11-3bcb909a6476>", line 28
myNewTable = max(x.items(), key=operator.itemgetter(1))[0] for x in myDict
^
SyntaxError: invalid syntax
What did I do wrong here ? And how could I fix this?
Well, your syntax is wrong, but you have the right idea. A nice and succinct solution using max and dict.get can be built on by iterating over myDict:
>>> {k : max(v, key=v.get) for k, v in myDict.items()}
{'bird': 'C', 'cat': 'A', 'cow': 'B', 'dog': 'B'}
You are using comprehension syntax but haven't wrapped it in a specific type (e.g. list => [], set => {}, dict => {:}). So fixing your code it would look like:
In []:
[max(myDict[x].items(), key=operator.itemgetter(1))[0] for x in myDict]
Out[]:
['B', 'A', 'B', 'C']
But you wanted it as a dict, so perhaps you meant:
In []:
{x: max(myDict[x].items(), key=operator.itemgetter(1))[0] for x in myDict}
Out[]:
{'bird': 'C', 'cat': 'A', 'cow': 'B', 'dog': 'B'}
But can be specified more succinctly (as per #coldspeed's answer):
{x: max(myDict[x], key=myDict[x].get) for x in myDict}
Another alternative using collections.Counter with its .most_common(...) method:
>>> from collections import Counter
>>> {k: Counter(v).most_common(1)[0][0] for k, v in myDict.items()}
{'cat': 'A', 'bird': 'C', 'cow': 'B', 'dog': 'B'}
This will be useful in general if you want to select more than one highest count elements from the nested dict. For example, below is the sample to select list of two most common keys in dict:
>>> {k: [x[0] for x in Counter(v).most_common(2)] for k, v in myDict.items()}
{'dog': ['B', 'C'], 'bird': ['C', 'B'], 'cat': ['A', 'C'], 'cow': ['B', 'C']}
From the Counter.most_common([n]) document:
Return a list of the n most common elements and their counts from the most common to the least. If n is omitted or None, most_common() returns all elements in the counter. Elements with equal counts are ordered arbitrarily:

Group list of dictionaries by value [duplicate]

This question already has answers here:
Grouping Python dictionary keys as a list and create a new dictionary with this list as a value
(2 answers)
Closed 4 years ago.
I have a list of dictionaries. How can i group that list by valaues.
list = [{a:1},{b:2},{c:1},{d:3},{e:2}]
Now my result should be like below
1:a,c
2:b,e
3:d
I tried using groupby from itertools. But i couldn't get the required result. I am using python 2.7.
Could you help me achieve this?
If you want to use groupby, the list has to be sorted by the same key you want to group by.
>>> lst = [{'a':1}, {'b':2}, {'c':1}, {'d':3}, {'e':2}]
>>> keyfunc = lambda d: next(iter(d.values()))
>>> sorted(lst, key=keyfunc)
[{'a': 1}, {'c': 1}, {'b': 2}, {'e': 2}, {'d': 3}]
>>> {k: [x for d in g for x in d]
... for k, g in itertools.groupby(sorted(lst, key=keyfunc), key=keyfunc)}
{1: ['a', 'c'], 2: ['b', 'e'], 3: ['d']}
Here's a possible solution without using any library.
def get_dict(list):
res = {}
for elem in list:
k, v = elem.keys(), elem.values()
if v[0] in res:
res[v[0]].append(k[0])
else:
res[v[0]] = [k[0]]
return res
With a list like yours, this would output a dictionary with the following format:
{ 1:[a,c], 2:[b, e], 3:[c] }
This is considering you're always going to have the same format as input. If not, you could just adjust what is read and saved.
This might help.
list = [{"a":1},{"b":2},{"c":1},{"d":3},{"e":2}]
d = {}
for i in list:
key, value = i.items()[0]
if value not in d:
d[value] = [key]
else:
d[value].append(key)
print(d)
Output:
{1: ['a', 'c'], 2: ['b', 'e'], 3: ['d']}
Tested in python2.7
Here is a way to do what you are looking for:
list_ = [{"a":1},{"b":2},{"c":1},{"d":3},{"e":2}]
values = set(value for dic in list_ for value in dic.values())
for value in values:
keys = [list(dic.keys())[0] for dic in list_ if value in dic.values()]
print("{}: {}".format(value, keys))
Output:
1: ['a', 'c']
2: ['b', 'e']
3: ['d']
Here's a solution that uses defaultdict.
from __future__ import print_function
from collections import defaultdict
lst = [{'a': 1}, {'b': 2}, {'c': 1}, {'d': 3}, {'e': 2}]
d = defaultdict(list)
for l in lst:
val, key = l.items()[0]
d[key].append(val)
print(d)
Output:
defaultdict(<type 'list'>, {1: ['a', 'c'], 2: ['b', 'e'], 3: ['d']})

Pythonic inverse dict non-unique mappings

I have a dictionary like this:
dict1 = {'a':[1,2,3], 'b':[1,2,3,4], 'c':[1,2]}
and want the inverse like this:
dict2 = dict({1:['a','b','c'], 2:['a','b','c'], 3:['a','b'], 4:['b']})
Like these questions:
Inverse Dict in Python \\
In-place dictionary inversion in Python
But I want to do it with non-unique keys and I don't want in-place conversion. I have some code working, but I was wondering if there's a dictionary comprehension way of doing this.
from collections import defaultdict
dict2 = defaultdict(list)
for i in dict1:
for j in dict1[i]:
dict2[j].append(i)
I tried this, but it only works for unique mappings. By unique I mean something like "for each value, there is only one key under which the value is listed". So unique mapping: '1: [a], 2: [b], 3: [c] -> a: [1], b: [2], c: [3]' VS non-unique mapping '1: [a], 2: [a, b], 3: [b, c] -> a: [1, 2], b: [2, 3], c: [3]'
dict2 = {j: i for i in dict1 for j in dict1[i]}
I think it must be something like this:
dict2 = {j: [i for i in dict1 if j in dict1[i]] for j in dict1[i]} # I know this doesn't work
Besides it not working, it seems like a comprehension like this would be inefficient. Is there an efficient, one liner way of doing this?
Standard dict:
>>> dict2 = {}
>>> for key, values in dict1.items():
... for value in values:
... dict2.setdefault(value, []).append(key)
...
>>> dict2
{1: ['a', 'c', 'b'], 2: ['a', 'c', 'b'], 3: ['a', 'b'], 4: ['b']}
defaultdict:
>>> dict2 = defaultdict(list)
>>> for key, values in dict1.items():
... for value in values:
... dict2[value].append(key)
...
>>> dict2
{1: ['a', 'c', 'b'], 2: ['a', 'c', 'b'], 3: ['a', 'b'], 4: ['b']}
I figured out an answer based on Vroomfondel's answer:
dict1 = {'a':[1,2,3], 'b':[1,2,3,4], 'c':[1,2]}
dict2 = {item: [key for key in dict1 if item in dict1[key]] for value in dict1.values() for item in value}
This isn't the fastest, but it is a one liner and it is not the slowest of the options presented!
from timeit import timeit
methods = [['Vroomfondel1', '''from collections import defaultdict
import itertools
dict1 = {'a':[1,2,3], 'b':[1,2,3,4], 'c':[1,2]}
dict2 = defaultdict(list)
for k,v in itertools.chain.from_iterable([itertools.product(vals,key) for key,vals in dict1.items()]):
dict2[k].append(v)'''],
['Vroomfondel2', '''from collections import defaultdict
import itertools
dict1 = {'a':[1,2,3], 'b':[1,2,3,4], 'c':[1,2]}
dict2 = defaultdict(list)
[dict2[k].append(v) for k,v in itertools.chain.from_iterable([itertools.product(vals,key) for key,vals in dict1.items()])]'''],
['***Vroomfondel2 mod', '''dict1 = {'a':[1,2,3], 'b':[1,2,3,4], 'c':[1,2]}
dict2 = {item: [key for key in dict1 if item in dict1[key]] for value in dict1.values() for item in value}'''],
['mhlester1', '''dict1 = {'a':[1,2,3], 'b':[1,2,3,4], 'c':[1,2]}
dict2 = {}
for key, values in dict1.items():
for value in values:
dict2.setdefault(value, []).append(key)'''],
['mhlester1 mod', '''from collections import defaultdict
dict1 = {'a':[1,2,3], 'b':[1,2,3,4], 'c':[1,2]}
dict2 = defaultdict(list)
for key, values in dict1.items():
for value in values:
dict2[value].append(key)'''],
['mhlester2', '''from collections import defaultdict
dict1 = {'a':[1,2,3], 'b':[1,2,3,4], 'c':[1,2]}
dict2 = defaultdict(list)
for key, values in dict1.items():
for value in values:
dict2[value].append(key)'''],
['initial', '''from collections import defaultdict
dict1 = {'a':[1,2,3], 'b':[1,2,3,4], 'c':[1,2]}
dict2 = defaultdict(list)
for i in dict1:
for j in dict1[i]:
dict2[j].append(i)''']
]
for method in methods:
print "% 15s" % (method[0]), '\t', timeit(method[1], number=10000)
prints out:
Vroomfondel1 0.202519893646
Vroomfondel2 0.164724111557
***Vroomfondel2 mod 0.114083051682
mhlester1 0.0599339008331
mhlester1 mod 0.091933965683
mhlester2 0.0900268554688
initial 0.0953099727631
As a one-liner (thanks to mhlesters input), but with so-so readability (and only working because the values in dict2 are mutable and thus setdefault returning a reference to them):
import itertools
[dict2.setdefault(k,[]).append(v) for k,v in itertools.chain.from_iterable([itertools.product(vals,[key]) for key,vals in dict1.items()])]
Or with a for loop:
import collections
import itertools
dict2=collections.defaultdict(list)
for k,v in itertools.chain.from_iterable([itertools.product(vals,[key]) for key,vals in dict1.items()]):
dict2[k].append(v)

Categories