How Do I sort a dict of dict in python??
I have a dictionary :
d = {
1: {2: 30, 3: 40, 4: 20, 6: 10},
2: {3: 30, 4: 60, 5: -60},
3: {1: -20, 5: 60, 6: 100},
}
How can I get the sorted (reverse) dict based on their values ?? How can I get the output like:
d = {
1: {3: 40, 2: 30, 4: 20, 6: 10},
2: {4: 60, 3: 30, 5: -60},
3: { 6: 100, 5: 60,1: -20},
}
What Padraic did, more compacted:
sort_vals = lambda d: OrderedDict(sorted(d.items(), key=lambda pair: pair[1], reversed=True))
d = dict((k, sort_vals(sub_dict)) for k, sub_dict in d.items())
(Not tested)
If you want order you need an OrderedDict of OrderedDicts, by chance the outer dict is in sorted order but normal dicts have no order:
from collections import OrderedDict
d = {
1: {2: 30, 3: 40, 4: 20, 6: 10},
2: {3: 30, 4: 60, 5: -60},
3: {1: -20, 5: 60, 6: 100},
}
keys = sorted(d.keys()) # sort outer dict keys
o = OrderedDict()
for k in keys: # loop over the sorted keys
# make an orderedDict out of the sorted items from each sub dict.
o[k] = OrderedDict((k,v) for k,v in sorted(d[k].items(),key=lambda x:x[1],reverse=True))
print(o)
OrderedDict([(1, OrderedDict([(3, 40), (2, 30), (4, 20), (6, 10)])), (2, OrderedDict([(4, 60), (3, 30), (5, -60)])), (3, OrderedDict([(6, 100), (5, 60), (1, -20)]))])
Related
I was trying sorting nested dict by inner dict's value. The sorting went well. but when I check my result, I found out that the original dict was printed when I just use the variable (d2), but it gives me the correct result when I use print(d2)
d2 = {1: {1: 4, 2: 5, 3: 6},
2: {7: 13, 8: 14, 9: 15, 10: 16, 11: 17, 12: 18},
3: {1: 1, 2: 9, 3: 4}}
# sorting by inner dict value
for keys in d2.keys():
sorted_tuples = sorted(d2[keys].items(), key=operator.itemgetter(1), reverse=True)
d2[keys] = {k: v for k, v in sorted_tuples}
print(d2)
d2
{1: {3: 6, 2: 5, 1: 4}, 2: {12: 18, 11: 17, 10: 16, 9: 15, 8: 14, 7: 13}, 3: {2: 9, 3: 4, 1: 1}}
{1: {1: 4, 2: 5, 3: 6},
2: {7: 13, 8: 14, 9: 15, 10: 16, 11: 17, 12: 18},
3: {1: 1, 2: 9, 3: 4}}
why the output is different when I use d2 and print(d2)
friend! Did you use the pretty print module to print the results of d2? I was only able to replicate your behavior using the pretty print module. Pretty print alphabetically sorts a dictionary before printing it, which can be disabled.
I originally (and wrongly) suspected the different output between d2 and print(d2) was a result of dictionaries being unordered collections of data; I suspected dict.__str__ and dict.__repr__ differed just enough. I would recommend you use an OrderedDict over a standard dictionary if you wish to maintain its order--despite Python preserving dictionaries insertion order in Python 3.7.
Below is my code and conclusions.
After initialization, d2 and print(d2) printed the same values:
❯ python
Python 3.7.12 (default, Sep 10 2021, 17:29:55)
[Clang 12.0.5 (clang-1205.0.22.9)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> d2 = {1: {1: 4, 2: 5, 3: 6},
2: {7: 13, 8: 14, 9: 15, 10: 16, 11: 17, 12: 18},
3: {1: 1, 2: 9, 3: 4}}
>>> d2
{1: {1: 4, 2: 5, 3: 6}, 2: {7: 13, 8: 14, 9: 15, 10: 16, 11: 17, 12: 18}, 3: {1: 1, 2: 9, 3: 4}}
>>> print(d2)
{1: {1: 4, 2: 5, 3: 6}, 2: {7: 13, 8: 14, 9: 15, 10: 16, 11: 17, 12: 18}, 3: {1: 1, 2: 9, 3: 4}}
After sorting, d2 and print(d2) printed the same values.
>>> import operator
>>> for keys in d2.keys():
... sorted_tuples = sorted(d2[keys].items(), key=operator.itemgetter(1), reverse=True)
... d2[keys] = {k: v for k, v in sorted_tuples}
...
>>> print(d2)
{1: {3: 6, 2: 5, 1: 4}, 2: {12: 18, 11: 17, 10: 16, 9: 15, 8: 14, 7: 13}, 3: {2: 9, 3: 4, 1: 1}}
>>> d2
{1: {3: 6, 2: 5, 1: 4}, 2: {12: 18, 11: 17, 10: 16, 9: 15, 8: 14, 7: 13}, 3: {2: 9, 3: 4, 1: 1}}
However, while using the pretty print module, I was able to replicate your behavior.
>>> from pprint import pprint as pp
>>> pp(print(d2))
{1: {3: 6, 2: 5, 1: 4}, 2: {12: 18, 11: 17, 10: 16, 9: 15, 8: 14, 7: 13}, 3: {2: 9, 3: 4, 1: 1}}
>>> pp(d2)
{1: {1: 4, 2: 5, 3: 6},
2: {7: 13, 8: 14, 9: 15, 10: 16, 11: 17, 12: 18},
3: {1: 1, 2: 9, 3: 4}}
Once I disabled dictionary sorting in the pretty print module, I was able to obtain your desired output.
>>> pprint.sorted = lambda x, key=None: x
>>> pp(d2)
{1: {3: 6, 2: 5, 1: 4},
2: {12: 18, 11: 17, 10: 16, 9: 15, 8: 14, 7: 13},
3: {2: 9, 3: 4, 1: 1}}
>>> pp(print(d2))
{1: {3: 6, 2: 5, 1: 4}, 2: {12: 18, 11: 17, 10: 16, 9: 15, 8: 14, 7: 13}, 3: {2: 9, 3: 4, 1: 1}}
I have a list of tuples with three elements:
A = [[(72, 1, 2), (96, 1, 4)],
[(72, 2, 1), (80, 2, 4)],
[],
[(96, 4, 1), (80, 4, 2), (70, 4, 5)],
[(70, 5, 4)],
]
I need to convert it to a dictionary in this format (note that the second element in the tuple will be the key):
A_dict = { 1: {2:72, 4:96},
2: {1:72, 4:80},
3: {},
4: {1:96, 2:80, 5:70},
5: {4:70},
}
Is there a way to convert A to A_dict?
I tried this:
A_dict = {b:{a:c} for a,b,c in A}
but I got an error:
ValueError: not enough values to unpack (expected 3, got 2)
You can just do:
A_dict = {k+1: {t[2]: t[0] for t in l} for k, l in enumerate(A)}
>>> A_dict
{
1: {2: 72, 4: 96},
2: {1: 72, 4: 80},
3: {},
4: {1: 96, 2: 80, 5: 70},
5: {4: 70}
}
By iterating on the indices of the list, according to its length. And for each value building its own dictionary:
A_dict = {i + 1 : {v[2] : v[0] for v in A[i]} for i in range(len(A))}
will output:
{1: {2: 72, 4: 96},
2: {1: 72, 4: 80},
3: {},
4: {1: 96, 2: 80, 5: 70},
5: {4: 70}}
Actually your desired code is:
A_dict = {A[i][0][1] : {v[2] : v[0] for v in A[i]} for i in range(len(A)) if len(A[i]) > 0}
But that will 'skip' the third line, as there is no list, thus not able to determinate the actual key, according to your specification.
I have this kind of dictionary in python:
x = {'test': {1: 2, 2: 4, 3: 5},
'this': {1: 2, 2: 3, 7: 6},
'is': {1: 2},
'something': {90: 2,92:3}}
I want to modify all of the value in the key by whatever value I want. Let's say 100, the methods I tried are below:
counter = 1
print(x)
for key,anotherKey in x.items():
while counter not in x[key]:
counter+=1
while counter in x[key]:
x[key][counter] = 100
counter+=1
counter =0
Which got the result below:
{'test': {1: 100, 2: 100, 3: 100},
'this': {1: 100, 2: 100, 7: 6},
'is': {1: 100},
'something': {90: 100,92: 3}}
I know why this is happening it's because the loop doesn't consider if the differences is more than 1 which in this case in 'this' : where the differences from 2 to 7 is more than 1. However I don't know how to solve this.
You can iterate via a nested for loop:
x = {'test': {1: 2, 2: 4, 3: 5},
'this': {1: 2, 2: 3, 7: 6},
'is': {1: 2},
'something': {90: 2,92:3}}
for a in x:
for b in x[a]:
x[a][b] = 100
print(x)
{'is': {1: 100},
'something': {90: 100, 92: 100},
'test': {1: 100, 2: 100, 3: 100},
'this': {1: 100, 2: 100, 7: 100}}
Or for a new dictionary you can use a dictionary comprehension:
res = {a: {b: 100 for b in x[a]} for a in x}
You can use dictionary comprehension in this way:
{a: {b:100 for b in d} for (a,d) in x.items()}
I'm trying to obtain the maximum value from every dictionary in a default dictionary of default dictionaries using Python3.
Dictionary Set Up:
d = defaultdict(lambda: defaultdict(int))
My iterator runs through the dictionaries and the csv data I'm using just fine, but when I call max, it doesn't necessarily return the max every time.
Example output:
defaultdict(<class 'int'>, {0: 106, 2: 35, 3: 12})
max = (0, 106)
defaultdict(<class 'int'>, {0: 131, 1: 649, 2: 338, 3: 348, 4: 276, 5: 150, 6: 138, 7: 89, 8: 54, 9: 22, 10: 5, 11: 2})
max = (0, 131)
defaultdict(<class 'int'>, {0: 39, 1: 13, 2: 30, 3: 15, 4: 5, 5: 10, 6: 1, 8: 1})
max = (0, 39)
defaultdict(<class 'int'>, {0: 40, 1: 53, 2: 97, 3: 80, 4: 154, 5: 203, 6: 173, 7: 142, 8: 113, 9: 76, 10: 55, 11: 22, 12: 13, 13: 7})
max = (0, 40)
So sometimes it's right, but far from perfect.
My approach was informed by the answer to this question, but I adapted it to try and make it work for a nested default dictionary. Here's the code I'm using to find the max:
for sub_d in d:
outer_dict = d[sub_d]
print(max(outer_dict.items(), key=lambda x: outer_dict.get(x, 0)))
Any insight would be greatly appreciated. Thanks so much.
If you check the values in outer_dict.items(), they are actually consisted of key value tuples, and since these aren't in your dictionary, they all return 0, and hence returns the index 0.
max(a.keys(),key = lambda x: a.get(x,0))
will get you the index of the max value, and retrieve the value by looking up on the dictionary
In
max(outer_dict.items(), key=lambda x: outer_dict.get(x, 0))
the outer_dict.items() call returns an iterator that produces (key, value) tuples of the items in outer_dict. So the key function gets passed a (key, value) tuple as its x argument, and then tries to find that tuple as a key in outer_dict, and of course that's not going to succeed, so the get call always returns 0.
Instead, we can use a key function that extracts the value from the tuple. eg:
nested = {
'a': {0: 106, 2: 35, 3: 12},
'b': {0: 131, 1: 649, 2: 338, 3: 348, 4: 276, 5: 150, 6: 138, 7: 89,
8: 54, 9: 22, 10: 5, 11: 2},
'c': {0: 39, 1: 13, 2: 30, 3: 15, 4: 5, 5: 10, 6: 1, 8: 1},
'd': {0: 40, 1: 53, 2: 97, 3: 80, 4: 154, 5: 203, 6: 173, 7: 142,
8: 113, 9: 76, 10: 55, 11: 22, 12: 13, 13: 7},
}
for k, subdict in nested.items():
print(k, max((t for t in subdict.items()), key=lambda t: t[1]))
output
a (0, 106)
b (1, 649)
c (0, 39)
d (5, 203)
A more efficient alternative to that lambda is to use itemgetter. Here's a version that puts the maxima into a dictionary:
from operator import itemgetter
nested = {
'a': {0: 106, 2: 35, 3: 12},
'b': {0: 131, 1: 649, 2: 338, 3: 348, 4: 276, 5: 150, 6: 138, 7: 89,
8: 54, 9: 22, 10: 5, 11: 2},
'c': {0: 39, 1: 13, 2: 30, 3: 15, 4: 5, 5: 10, 6: 1, 8: 1},
'd': {0: 40, 1: 53, 2: 97, 3: 80, 4: 154, 5: 203, 6: 173, 7: 142,
8: 113, 9: 76, 10: 55, 11: 22, 12: 13, 13: 7},
}
ig1 = itemgetter(1)
maxes = {k: max((t for t in subdict.items()), key=ig1)
for k, subdict in nested.items()}
print(maxes)
output
{'a': (0, 106), 'b': (1, 649), 'c': (0, 39), 'd': (5, 203)}
We define ig1 outside the dictionary comprehension so that we don't call itemgetter(1) on every iteration of the outer loop.
When I try the following list creation, I see that each element refers to a different object.
>>> a = [1] * 7
>>> a[0] = 2
>>> a
Out[17]: [2, 1, 1, 1, 1, 1, 1]
However, when I try the following list creation with dictionaries, I see that each element refers to the same object.
a = [{1:10, 2:20}] * 7
a[0][1] = 30
a
Out[20]:
[{1: 30, 2: 20},
{1: 30, 2: 20},
{1: 30, 2: 20},
{1: 30, 2: 20},
{1: 30, 2: 20},
{1: 30, 2: 20},
{1: 30, 2: 20}]
How can I create this second list as easy as possible, but with the elements copies instead of references?
>>> a = [{1:10, 2:20} for _ in range(7)]
>>> a[0][1] = 30
>>> a
[{1: 30, 2: 20}, {1: 10, 2: 20}, {1: 10, 2: 20}, {1: 10, 2: 20}, {1: 10, 2: 20}, {1: 10, 2: 20}, {1: 10, 2: 20}]
Actually when you create a list with multiplication python will creating a list with repeated items with same memory address :
>>> a=[1]*7
>>> id(a[0])
40968536
>>> id(a[1])
40968536
>>> a = [{1:10, 2:20}]*7
>>> id(a[0])
140379402804592
>>> id(a[1])
140379402804592
But when you want to change an immutable object like (numbers,tuple,string,...) python will change the reference not the object!
And when you change a mutable object python will change the object itself,so all the entries will change.
So as a more efficient way in python 2 you can use xrange (xrange return a generator instead a list) (in 3 range ) within a list comprehension to create your list :
>>> a = [{1:10, 2:20} for _ in xrange(7)]
>>> a
[{1: 10, 2: 20}, {1: 10, 2: 20}, {1: 10, 2: 20}, {1: 10, 2: 20}, {1: 10, 2: 20}, {1: 10, 2: 20}, {1: 10, 2: 20}]
>>> a[0][1]=0
>>> a
[{1: 0, 2: 20}, {1: 10, 2: 20}, {1: 10, 2: 20}, {1: 10, 2: 20}, {1: 10, 2: 20}, {1: 10, 2: 20}, {1: 10, 2: 20}]
>>> id(a[0])
140379402734400
>>> id(a[1])
140379402809424