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:
Related
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']})
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']})
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'}
I want to merge two dictionaries like this:
old={'a':'1a','b':'1b','c':'1c'}
new={'1a':'c','1b':'d','1c':'e'}
I want output like this:
new_dict={'a':'c','b':'d','c':'e'}
Note: The length of both dictionaries is different.
How to do it in python?
With a dict-comprehension:
old = {'a': '1a','b': '1b','c': '1c'}
new = {'1a': 'c','1b': 'd','1c': 'e'}
res = {k: new[v] for k, v in old.items()} # if all values in `old` exist in `new` as keys.
res = {k: new.get(v, None) for k, v in old.items()} # if you cannot guarantee the above.
print(res) # {'b': 'd', 'a': 'c', 'c': 'e'}
*Note that the None parameter of the .get() method is the default one and as such, it can be omitted. I will leave it there though to remind you that you can specify anything you want depending on the specifics of your problem (e.g., '' (blank string) might be better in your case)
You can get the new dictionary using a dictionary comprehension where you get the values from the new dictionary based on the keys in the old dictionary. Be sure to use get which returns None by default if a value from the old dictionary is not present as a key in the new dictionary.
old = {'a': '1a', 'b': '1b' ,'c': '1c'}
new = {'1a': 'c', '1b': 'd', '1c': 'e'}
new_dict = {k: new.get(old[k]) for k in old}
>>> new_dict
{'a': 'c', 'b': 'd', 'c': 'e'}
You could use a dictionary comprehension that interprets the value of the first dictionary as the key of the second dictionary:
>>> {item: new[value] for item, value in old.items() if value in new}
{'a': 'c', 'b': 'd', 'c': 'e'}
In case you can garantuee that all values of old are in new you could omit the if value in new part.
I have a dictionary
dict = {'a': 'cat', 'b':'dog'}
and I want to replace the keys in the dict
with new keys (or key labels) from a list ['c', 'd'] so that I get (the same)
dict = {'c': 'cat', 'd':'dog'}. How can I do this?
You can define the relation between the old keys and their replacements, in another dictionary, like this. Here, mapping is the dictionary which maps the old keys with the new keys.
d, mapping = {'a': 'cat', 'b':'dog'}, {"a":"c", "b":"d"}
print {mapping[k]:v for k, v in d.items()}
Output
{'c': 'cat', 'd': 'dog'}
As already has been pointed out, a dictionary is not ordered. So if you want to replace your keys with values from a list (which is ordered), you will need to specify how the keys of your dict are ordered. Something along these lines:
def replaceKeys (d, newKeys, sort):
return {newKeys[idx]: v for idx, (_, v)
in enumerate(sorted(d.items(), key = lambda kv: sort(kv[0])))}
d = {'cat': 'gato', 'dog': 'chucho', 'mule': 'mula'}
d2 = replaceKeys(d, ['a', 'b', 'c'], lambda oldKey: oldKey) #sort alphabetically
print(d2)
d2 = replaceKeys(d, ['a', 'b', 'c'], lambda oldKey: -len(oldKey)) #sort by length ascending
print(d2)
d2 = replaceKeys(d, ['a', 'b', 'c'], lambda oldKey: oldKey[2]) #sort by third letter
print(d2)