Sort a dictionary by values in a nested dictionary - python

I have dictionary like so:
dic = {"first_a" : { "first_b" : {10, 2} } , "second_a" : {"second_b" : {13, 15} } [...] }
I would like to sort the nested dictionary according to the sum of first_b and second_b values.
I can't get my head around this one, could someone provide an helping hand ?
I have tried to use the sorted() function but wasn't able to find the right lambda function to use as key..

Assuming that you meant to have a dictionary like this:
data = {'a': {'b': {2, 10}}, 'c': {'d': {13, 15}}}
You can get what you want like this:
sorted(data, key =lambda k: sum(*dic[k].values()), reverse=True)
However I don't consider this very readable. I would instead do:
def get_sum(k):
vals, *_ = data[k].values()
return sum(vals)
sorted(data, key=get_sum, reverse=True)
When I'm looking at code late at night, too many parentheses == too long to figure out what's happening.
Note that I used values() because I didn't know if your inner keys were constant. If they were, life is even easier. Note this operates on and sorts the keys.

Related

Retrieving items from a nested dictionary with a nested for loop fresults in KeyError

I need to systematically access dictionaries that are nested within a list within a dictionary at the 3rd level, like this:
responses = {'1': {'responses': [{1st dict to be retrieved}, {2nd dict to be retrieved}, ...]},
'2': {'responses': [{1st dict to be retrieved}, {2nd dict to be retrieved}, ...]}, ...}
I need to unnest and transform these nested dicts into dataframes, so the end result should look like this:
responses = {'1': df1,
'2': df2, ...}
In order to achieve this, I built a for-loop in order to loop through all keys on the first level. Within that loop, I am using another loop to extract each item from the nested dicts into a new empty list called responses_df:
responses_dict = {}
for key in responses.keys():
for item in responses[key]['responses']:
responses_dict[key].update(item)
However, I get:
KeyError: '1'
The inner loop works if I use it individually on a key within the dict, but that doesn't really help me since the data comes from an API and has to be updated dynamically every few minutes in production.
The nex loop to transform the result into dataframes would look like this:
for key in responses_dict:
responses_df[key] = pd.DataFrame.from_dict(responses_dict[key], orient='index')
But I haven't gotten to try that out since the first operation fails.
Try this:
from collections import defaultdict
responses_dict = defaultdict(dict) # instead of {}
Then your code will work.
In fact responses_dict[key] where key=1 doesn't exist.
So when you simply do print(responses_dict[key]) you get the same error, 1 is not a key of that dict and update is not used as it should be.
Try the following syntax :
responses_dict = {}
for key in responses.keys():
print(key)
for item in responses[key]['responses']:
responses_dict.update(key = item)
I prefer using dictionaries while updating a dictionary.
If you update with an existing key, the value of that key will be updated.
If you update with an new key-value pair, the pair will be added to that dictionary.
>>>d1 = {1: 10, 2:20}
>>>d1.update({1:20})
>>>d1
>>>{1: 20, 2:20}
>>>d1.update({3:30})
>>>d1
>>>{1: 20, 2:20, 3:30}
Try fixing your line with:
responses_dict = {}
for key in responses.keys():
for item in responses[key]['responses']:
responses_dict.update({key: item})
So basically, use dictionary to update a dictionary, more readable and easy.
Try this:
responses = {'1': {'responses': [{'a': 1, 'b': 2}, {'c': 3, 'd': 4}]},
'2': {'responses': [{'e': 5}, {'f': 6}]}}
result = {k: pd.DataFrame(chain.from_iterable(v['responses'])) for k, v in responses.items()}
for df in result.values():
print(df, end='\n\n')
Output:
0
0 a
1 b
2 c
3 d
0
0 e
1 f

Python: How to sort a dictionary of X and Y coordinates by ascending X coordinate value?

I have the following dictionary that I would like to sort based on their X coordinate in ascending fashion so that I can identify the "beacon" by the color arrangement (RGB in different orders). I keep trying to sort it like a list but that's not working out too well. Thanks in advance :)
Beacon2 = {
'r': [998.9282836914062, 367.3825378417969],
'b': [985.82373046875, 339.2225646972656],
'g': [969.539794921875, 369.2041931152344]
}
For this specific dictionary the expected result is
sortedBeacon = {
'g': [969.539794921875, 369.2041931152344],
'b': [985.82373046875, 339.2225646972656],
'r': [998.9282836914062, 367.3825378417969]
}
Note that dictionaries in general are not sortable. You can generate the internals sorted however without any lambdas by using itemgetter:
from operator import itemgetter
sorted_d = sorted(d.items(), key=itemgetter(1))
If you really want to maintain order, wrap the above in an OrderedDict
The method sort() in Python is normally used on lists and tuples whereas sorted() is better for data structures like dictionaries.
In this case, using a simple lambda function can help you get what you want.
print(sorted(Beacon2.values(), key = lambda x: (x[0]))
You can try this:
from collections import OrderedDict
Beacon2 = {'r': [998.9282836914062, 367.3825378417969], 'b':
[985.82373046875, 339.2225646972656], 'g': [969.539794921875, 369.2041931152344]}
sorted_beacons = sorted(Beacon2.items(), key = lambda x: x[1][0])
>>> print(OrderedDict(sorted_beacons))
OrderedDict([('g', [969.539794921875, 369.2041931152344]), ('b', [985.82373046875, 339.2225646972656]), ('r', [998.9282836914062, 367.3825378417969])])
Where you first sort the list of tuples from Beacon2.items(), with a sorting key applied on the X coordinate located at [1][0] of each tuple.
Note that you need to wrap an OrderedDict to your result to preserve order of the dictionary.
If you just want the values, use this:
sorted(data.values())
If you want the keys associated with the sorted values, use this:
sorted(data, key=data.get)
Both key and values:
sorted(data.items(), key=lambda x:x[1])
Courtesy of: sort dict by value python

Merging values from 2 dictionaries (Python)

(I'm new to Python!)
Trying to figure out this homework question:
The function will takes a​s input​ two dictionaries, each mapping strings to integers. The function will r​eturn​ a dictionary that maps strings from the two input dictionaries to the sum of the integers in the two input dictionaries.
my idea was this:
def ​add(​dicA,dicB):
dicA = {}
dicB = {}
newdictionary = dicA.update(dicB)
however, that brings back None.
In the professor's example:
print(add({'alice':10, 'Bob':3, 'Carlie':1}, {'alice':5, 'Bob':100, 'Carlie':1}))
the output is:
{'alice':15, 'Bob':103, 'Carlie':2}
My issue really is that I don't understand how to add up the values from each dictionaries. I know that the '+' is not supported with dictionaries. I'm not looking for anyone to do my homework for me, but any suggestions would be very much appreciated!
From the documentation:
update([other])
Update the dictionary with the key/value pairs from other, overwriting existing keys. Return None.
You don't want to replace key/value pairs, you want to add the values for similar keys. Go through each dictionary and add each value to the relevant key:
def ​add(​dicA,dicB):
result = {}
for d in dicA, dicB:
for key in d:
result[key] = result.get(key, 0) + d[key]
return result
result.get(key, 0) will retrieve the value of an existing key or produce 0 if key is not yet present.
First of all, a.update(b) updates a in place, and returns None.
Secondly, a.update(b) wouldn't help you to sum the keys; it would just produce a dictionary with the resulting dictionary having all the key, value pairs from b:
>>> a = {'alice':10, 'Bob':3, 'Carlie':1}
>>> b = {'alice':5, 'Bob':100, 'Carlie':1}
>>> a.update(b)
>>> a
{'alice': 5, 'Carlie': 1, 'Bob': 100}
It'd be easiest to use collections.Counter to achieve the desired result. As a plus, it does support addition with +:
from collections import Counter
def add(dicA, dicB):
return dict(Counter(dicA) + Counter(dicB))
This produces the intended result:
>>> print(add({'alice':10, 'Bob':3, 'Carlie':1}, {'alice':5, 'Bob':100, 'Carlie':1}))
{'alice': 15, 'Carlie': 2, 'Bob': 103}
The following is not meant to be the most elegant solution, but to get a feeling on how to deal with dicts.
dictA = {'Alice':10, 'Bob':3, 'Carlie':1}
dictB = {'Alice':5, 'Bob':100, 'Carlie':1}
# how to iterate through a dictionary
for k,v in dictA.iteritems():
print k,v
# make a new dict to keep tally
newdict={}
for d in [dictA,dictB]: # go through a list that has your dictionaries
print d
for k,v in d.iteritems(): # go through each dictionary item
if not k in newdict.keys():
newdict[k]=v
else:
newdict[k]+=v
print newdict
Output:
Bob 3
Alice 10
Carlie 1
{'Bob': 3, 'Alice': 10, 'Carlie': 1}
{'Bob': 100, 'Alice': 5, 'Carlie': 1}
{'Bob': 103, 'Alice': 15, 'Carlie': 2}
def ​add(​dicA,dicB):
You define a function that takes two arguments, dicA and dicB.
dicA = {}
dicB = {}
Then you assign an empty dictionary to both those variables, overwriting the dictionaries you passed to the function.
newdictionary = dicA.update(dicB)
Then you update dicA with the values from dicB, and assign the result to newdictionary. dict.update always returns None though.
And finally, you don’t return anything from the function, so it does not give you any results.
In order to combine those dictionaries, you actually need to use the values that were passed to it. Since dict.update mutates the dictionary it is called on, this would change one of those passed dictionaries, which we generally do not want to do. So instead, we use an empty dictionary, and then copy the values from both dictionaries into it:
def add (dicA, dicB):
newDictionary = {}
newDictionary.update(dicA)
newDictionary.update(dicB)
return newDictionary
If you want the values to sum up automatically, then use a Counter instead of a normal dictionary:
from collections import Counter
def add (dicA, dicB):
newDictionary = Counter()
newDictionary.update(dicA)
newDictionary.update(dicB)
return newDictionary
I suspect your professor wants to achieve this using more simple methods. But you can achieve this very easily using collections.Counter.
from collections import Counter
def add(a, b):
return dict(Counter(a) + Counter(b))
Your professor probably wants something like this:
def add(a, b):
new_dict = copy of a
for each key/value pair in b
if key in new_dict
add value to value already present in new_dict
else
insert key/value pair into new_dict
return new_dict
You can try this:
def add(dict1, dict2):
return dict([(key,dict1[key]+dict2[key]) for key in dict1.keys()])
I personally like using a dictionary's get method for this kind of merge:
def add(a, b):
result = {}
for dictionary in (a, b):
for key, value in dictionary.items():
result[key] = result.get(key, 0) + value
return result

Sorting with a python dictionary

I have a dictionary of "documents" in python with document ID numbers as keys and dictionaries (again) as values. These internal dictionaries each have a 'weight' key that holds a floating-point value of interest. In other words:
documents[some_id]['weight'] = ...
What I want to do is obtain a list of my document IDs sorted in descending order of the 'weight' value. I know that dictionaries are inherently unordered (and there seem to be a lot of ways to do things in Python), so what is the most painless way to go? It feels like kind of a messy situation...
I would convert the dictionary to a list of tuples and sort it based on weight (in reverse order for descending), then just remove the objects to get a list of the keys
l = documents.items()
l.sort(key=lambda x: x[1]['weight'], reverse=True)
result = [d[0] for d in l]
I took the approach that you might want the keys as well as the rest of the object:
# Build a random dictionary
from random import randint
ds = {} # A |D|ata |S|tructure
for i in range(20,1,-1):
ds[i]={'weight':randint(0,100)}
sortedDS = sorted(ds.keys(),key=lambda x:ds[x]['weight'])
for i in sortedDS :
print i,ds[i]['weight']
sorted() is a python built in that takes a list and returns it sorted (obviously), however it can take a key value that it uses to determine the rank of each object. In the above case it uses the 'weight' value as the key to sort on.
The advantage of this over Ameers answer is that it returns the order of keys rather than the items. Its an extra step, but it means you can refer back into the original data structure
This seems to work for me. The inspiration for it came from OrderedDict and question #9001509
from collections import OrderedDict
d = {
14: {'weight': 90},
12: {'weight': 100},
13: {'weight': 101},
15: {'weight': 5}
}
sorted_dict = OrderedDict(sorted(d.items(), key=lambda rec: rec[1].get('weight')))
print sorted_dict

Find only common key-value pairs of several dicts: dict intersection

I have 0 or more dicts in a list:
>>> dicts = [dict(a=3, b=89, d=2), dict(a=3, b=89, c=99), dict(a=3, b=42, c=33)]
I want to create a new dict that contains only keys that are in all the above dicts, and only if the values are all the same:
>>> dict_intersection(*dicts)
{"a": 3}
I feel that there should be an elegant way of writing dict_intersection, but I'm only coming up with inelegant and/or inefficient solutions myself.
>>> dict(set.intersection(*(set(d.iteritems()) for d in dicts)))
{'a': 3}
Note: This solution requires the dictionary values to be hashable, in addition to the keys.
Since the key/value pairs must already be in the first dict, you can iterate over this dict's items.
dict(pair for pair in dicts[0].items()
if all((pair in d.items() for d in dicts[1:])))
Looks less elegant than interjay's answer, but works without the restriction of hashable values.
Edit: Changed the all expression to a generator expression for speed improvement
How's this?
def intersect_two_dicts (d1, d2):
return { k:v for k,v in d1.iteritems() if ((k in d2)and(d1[k]==d2[k])) }
def intersect_dicts (list_of_dicts):
return reduce(intersect_two_dicts, list_of_dicts)
# Tests
dicts = [dict(a=3, b=89, d=2), dict(a=3, b=89, c=99), dict(a=3, b=42, c=33)]
print (intersect_two_dicts(dicts[0], dicts[1]))
print (intersect_dicts(dicts))
Edit(1): I'm not sure which of these is fastest. The set.intersection solutions are certainly most elegant (short one liners!) but I would be interested to see some benchmarking.
Edit(2): Bonus - get any dictionary entries whose (key:value) pairs are common to any two dictionaries:
{k:count for k,count in
collections.Counter(itertools.chain(*[d.iteritems() for d in dicts])).iteritems()
if count > 1}
>>> dicts
[{'a': 3, 'b': 89, 'd': 2}, {'a': 3, 'c': 99, 'b': 89}, {'a': 3, 'c': 33, 'b': 42}]
>>> sets = (set(d.iteritems()) for d in dicts)
>>> dict_intersection = dict(set.intersection(*sets))
>>> dict_intersection
{'a': 3}
A slightly more hands-dirty approach: Take the list of keys for each dictionary, sort each list, and then proceed as if you were merging them (keep an index for each list, advance the one w/ the lowest value). Whenever all of the indices point to the same key, check the values for equality; either way, advance all indices.

Categories

Resources