Dictionary multiple in Python - python

I have a multidictionary:
{'a': {'b': {'c': {'d': '2'}}},
'b': {'b': {'c': {'d': '7'}}},
'c': {'b': {'c': {'d': '3'}}},
'f': {'d': {'c': {'d': '1'}}}}
How can I sort it based on the values '2' '3' '7' '1'
so my output will be:
f.d.c.d.1
a.b.c.d.2
c.b.c.d.3
b.b.c.d.7

You've got a fixed-shape structure, which is pretty simple to sort:
>>> d = {'a': {'b': {'c': {'d': '2'}}}, 'c': {'b': {'c': {'d': '3'}}}, 'b': {'b': {'c': {'d': '7'}}}, 'f': {'d': {'c': {'d': '1'}}}}
>>> sorted(d, key=lambda x: d[x].values()[0].values()[0].values()[0])
['f', 'a', 'c', 'b']
>>> sorted(d.items(), key=lambda x: x[1].values()[0].values()[0].values()[0])
[('f', {'d': {'c': {'d': '1'}}}),
('a', {'b': {'c': {'d': '2'}}}),
('c', {'b': {'c': {'d': '3'}}}),
('b', {'b': {'c': {'d': '7'}}})]
Yes, this is a bit ugly and clumsy, but only because your structure is inherently ugly and clumsy.
In fact, other than the fact that d['f'] has a key 'd' instead of 'b', it's even more straightforward. I suspect that may be a typo, in which case things are even easier:
>>> d = {'a': {'b': {'c': {'d': '2'}}}, 'c': {'b': {'c': {'d': '3'}}}, 'b': {'b': {'c': {'d': '7'}}}, 'f': {'b': {'c': {'d': '1'}}}}
>>> sorted(d.items(), key=lambda x:x[1]['b']['c']['d'])
[('f', {'b': {'c': {'d': '1'}}}),
('a', {'b': {'c': {'d': '2'}}}),
('c', {'b': {'c': {'d': '3'}}}),
('b', {'b': {'c': {'d': '7'}}})]
As others have pointed out, this is almost certainly not the right data structure for whatever it is you're trying to do. But, if it is, this is how to deal with it.
PS, it's confusing to call this a "multidictionary". That term usually means "dictionary with potentially multiple values per key" (a concept which in Python you'd probably implement as a defaultdict with list or set as its default). A single, single-valued dictionary that happens to contain dictionaries is better named a "nested dictionary".

In my opinion this kind of design is very hard to read and maintain. Can you consider replacing the internal dictionaries with string-names?
E.g.:
mydict = {
'a.b.c.d' : 2,
'b.b.c.d' : 7,
'c.b.c.d' : 3,
'f.d.c.d' : 1,
}
This one is much easier to sort and waaaay more readable.
Now, a dictionary is something unsortable due to its nature. Thus, you have to sort an e.g. a list representation of it:
my_sorted_dict_as_list = sorted(mydict.items(),
key=lambda kv_pair: kv_pair[1])

you can do it recursively:
d = {'a': {'b': {'c': {'d': '2'}}}, 'c': {'b': {'c': {'d': '3'}}}, 'b': {'b': {'c': {'d': '7'}}}, 'f': {'d': {'c': {'d': '1'}}}}
def nested_to_string(item):
if hasattr(item, 'items'):
out = ''
for key in item.keys():
out += '%s.' % key + nested_to_string(item[key])
return out
else:
return item + '\n'
print nested_to_string(d)
or
def nested_to_string(item):
def rec_fun(item, temp, res):
if hasattr(item, 'items'):
for key in item.keys():
temp += '%s.' % key
rec_fun(item[key], temp, res)
temp = ''
else:
res.append(temp + item)
res = []
rec_fun(d, '', res)
return res
why do you want to do this.

Your data structure is basically a multi-level tree, so a good way to do what you want is to do what is called a depth-first traversal of it, which can be done recursively, and then massage the intermediate results a bit to sort and format them them into the desired format.
multidict = {'a': {'b': {'c': {'d': '2'}}},
'b': {'b': {'c': {'d': '7'}}},
'c': {'b': {'c': {'d': '3'}}},
'f': {'d': {'c': {'d': '1'}}}}
def nested_dict_to_string(nested_dict):
chains = []
for key,value in nested_dict.items():
chains.append([key] + visit(value))
chains = ['.'.join(chain) for chain in sorted(chains, key=lambda chain: chain[-1])]
return '\n'.join(chains)
def visit(node):
result = []
try:
for key,value in node.items():
result += [key] + visit(value)
except AttributeError:
result = [node]
return result
print nested_dict_to_string(multidict)
Output:
f.d.c.d.1
a.b.c.d.2
c.b.c.d.3
b.b.c.d.7

Related

Find column names and their respective value in a pandas data frame which matches a condition and store the result in a dictionary

I have a pandas dataframe (called df) where I search for each row,(i.e. on a given date) the columns which have values less than 0.5. In the below screenshot, I have highlighted the values (in yellow) which are less than 0.5.
The df dataframe is as follows:
data = {'Date': ['2020-12-22','2020-12-23','2020-12-24','2020-12-25'],
'A': ['0.065','0.965','0.363','0.774'],
'B': ['0.292','0.367','0.396','0.484'],
'C': ['0.078','0.489','0.095','0.781'],
'D': ['0.703','0.748','0.631','0.612']}
df = config.pd.DataFrame (data, columns = ['Date','A', 'B', 'C', 'D'])
I would like to store the result in a dictionary which should look similar to the below nested dictionary:
Could someone help me with a sample code.
Try the following. I understand that the res=... line is not very readable, as most of dict comprehensions are, but it's more concise. If you need a more redable solution, it can be easily done with a couple of loops-ifs
d=df.to_dict(orient='records')
res={i['Date']:{k:float(i[k]) for k in i if k!='Date' and float(i[k])<0.5} for i in d}
>>>print(res)
{'2020-12-22': {'A': 0.065, 'B': 0.292, 'C': 0.078}, '2020-12-23': {'B': 0.367, 'C': 0.489}, '2020-12-24': {'A': 0.363, 'B': 0.396, 'C': 0.095}, '2020-12-25': {'B': 0.484}}
If you want to use loops to construct the result, you can do the following:
d=df.to_dict(orient='records')
for i in d:
temp={}
for k in i:
if k!='Date' and float(i[k])<0.5:
temp[k]=float(i[k])
res[i['Date']]=temp
>>>print(res)
{'2020-12-22': {'A': 0.065, 'B': 0.292, 'C': 0.078}, '2020-12-23': {'B': 0.367, 'C': 0.489}, '2020-12-24': {'A': 0.363, 'B': 0.396, 'C': 0.095}, '2020-12-25': {'B': 0.484}}
If I understand your correctly, you could use to_dict to generate the results and filter with a dictionary comprehension:
import pprint
# set Date as index
n_df = df.set_index('Date').astype(float)
# use to_dict('index')
res = {k: {ki: vi for ki, vi in d.items() if vi < 0.5} for k, d in n_df.to_dict('index').items()}
pprint.pprint(res)
Output
{'2020-12-22': {'A': 0.065, 'B': 0.292, 'C': 0.078},
'2020-12-23': {'B': 0.367, 'C': 0.489},
'2020-12-24': {'A': 0.363, 'B': 0.396, 'C': 0.095},
'2020-12-25': {'B': 0.484}}

Appending a dictionary of dictionaries with another dictionary of dictionaries not getting order correct [duplicate]

This question already has answers here:
How do I merge two dictionaries in a single expression in Python?
(43 answers)
Closed 2 years ago.
I have 4 python dictionaries where the value of each key is another dictionary:
a={'a': {'e': 4}}
b={'b': {'f': 5}}
c={'c': {'g': 6}}
d={'d': {'h': 7}}
I want to merge dictionaries a, b, c and d together so that I have one final dictionary that looks like:
{'a': {'e': 4}, 'b': {'f': 5}, 'c': {'g': 6}, 'd': {'h': 7}}
where the order of the parent dictionary is in the order in which I add each original dictionary.
So I created an empty dictionary and did this:
x={} #create empty dictionary
x.update(a) #update with first dictionary
print x
x.update(b) #update with second dictionary
print x
x.update(c) #update with third dictionary
print x
x.update(d) #update with forth dictionary
print x
The result is this:
{'a': {'e': 4}}
{'a': {'e': 4}, 'b': {'f': 5}}
{'a': {'e': 4}, 'c': {'g': 6}, 'b': {'f': 5}}
{'a': {'e': 4}, 'c': {'g': 6}, 'b': {'f': 5}, 'd': {'h': 7}}
I am not sure why, after the third update, that c is added to x between a and b. And then after the forth update, d somehow gets added at the end. It seems random.
Keep in mind, that sorted will not work. The above is an example of what I want and my keys may not always be in alphabetical order. I simple want the order in which I added each dictionary.
Edit: This is for python 2.7, but appreciate answers for 3.6 as I will be migrating a tool from 2 to 3 in the near future.
You can just unpack each dict into one (in python3.6+ it will retain order of insertion):
>>> {**a, **b, **c, **c, **d}
{'a': {'e': 4}, 'b': {'f': 5}, 'c': {'g': 6}, 'd': {'h': 7}}
And in python 3.9+ you could use the merge operator:
>>> a | b | c | d
{'a': {'e': 4}, 'b': {'f': 5}, 'c': {'g': 6}, 'd': {'h': 7}}
EDIT
This also works for OrderedDicts. As you are using python 2.7 this will retain order:
>>> from collections import OrderedDict
>>> OrderedDict(**a, **b, **c, **d)
OrderedDict([('a', {'e': 4}), ('b', {'f': 5}), ('c', {'g': 6}), ('d', {'h': 7})])
"A dictionary is a collection which is unordered, changeable and indexed." - https://www.w3schools.com/python/python_dictionaries.asp, so its only natural that they might not be in the desired order.
Use OrderedDict from the collections module if you want your dictionary to keep the order in which keys are inserted.
Dictionaries have no sense of order in Python versions earlier than version 3.6.
Use an OrderedDict if you want to preserve the insertion order:
from collections import OrderedDict
a = {"a": {"e": 4}}
b = {"b": {"f": 5}}
c = {"c": {"g": 6}}
d = {"d": {"h": 7}}
x.update(a)
x.update(b)
x.update(c)
x.update(d)
assert x.keys() == ["a", "b", "c", "d"]

python list of dicts - convert each key-value as a individual dict

with a list like below that has one or more dicts
l = [{'b': 'h', 'c': (1,2)}, {'d': [0, 1], 'e': {'f': 2, 'g': 'i'} } ]
need to extract each key-value pair as an individual dict
Expected output
[{'b': 'h'}, {'c': (1,2)}, {'d': [0, 1]}, {'e': {'f': 2, 'g': 'i'} } ]
I have been trying to do this via list comprehension - the outer comprehension could be something like [ {k,v} for k, v in ?? - need some help in getting the inner comprehension.
I believe this is what you're looking for - except that the order of the elements might be different, but that's to be expected when dealing with dictionaries:
lst = [{'b': 'h', 'c': (1,2)}, {'d': [0, 1], 'e': {'f': 2, 'g': 'i'}}]
[{k: v} for d in lst for k, v in d.items()]
=> [{'c': (1, 2)}, {'b': 'h'}, {'e': {'g': 'i', 'f': 2}}, {'d': [0, 1]}]
This should work:
[{k: v} for i in l for k, v in i.items()]

python update nested dictionary with other nested dictionary [duplicate]

This question already has answers here:
How to update values in nested dictionary if keys are in a list? [duplicate]
(5 answers)
Closed 3 years ago.
I want to use dd1 and dd2 to update dd3
dd1 = {'a': {'b': [{'x':1}]}}
dd2 = {'a': {'c': [{'x':2}]}}
dd3 = {'a': {'b': {}, 'c': {}}}
so I get dd3:
dd3 = {'a': {'b': [{'x':1}], 'c': [{'x':2}]}}
I know how to update flat dictionary
d1 = {'a': 1, 'b': 2, 'c': 3}
d2 = {'a': 2, 'b': 3, 'd': 4}
d3 = defaultdict(list)
for k, v in chain(d1.items(), d2.items()):
d3[k].append(v)
but struggle to find clear way to update nested dictionary:
With recursive traversal (assuming that all source dicts have same depth levels and lists as final expected update values):
dd1 = {'a': {'b': [{'x':1}]}}
dd2 = {'a': {'c': [{'x':2}]}}
dd3 = {'a': {'b': {}, 'c': {}}}
def update_nested_dict(target, d1, d2):
for k,v in target.items():
d1_v, d2_v = d1.get(k, []), d2.get(k, [])
if isinstance(v, dict):
if not v:
target[k] = d1_v + d2_v
else:
update_nested_dict(v, d1_v, d2_v)
update_nested_dict(dd3, dd1, dd2)
print(dd3)
The output:
{'a': {'b': [{'x': 1}], 'c': [{'x': 2}]}}
You can use recursion:
dd1 = {'a': {'b': [{'x':1}]}}
dd2 = {'a': {'c': [{'x':2}]}}
dd3 = {'a': {'b': {}, 'c': {}}}
def update(d, *args):
return {a:(lambda x:x[0] if not b else update(b, *x))([i[a] for i in args if a in i])\
if isinstance(b, dict) else b for a, b in d.items()}
print(update(dd3, dd1, dd2))
Output:
{'a': {'b': [{'x': 1}], 'c': [{'x': 2}]}}
This solution can handle multiple dictionaries of varying depths:
dd1 = {'a': {'b':{'d':{'e':[{'x':1}]}}}}
dd2 = {'a': {'c': [{'x':2}]}}
dd4 = {'a':{'j':{'k':[{'x':3}]}}}
dd3 = {'a': {'b': {'d':{'e':{}}}, 'c': {}, 'j':{'k':{}}}}
print(update(dd3, dd1, dd2, dd4))
Output:
{'a': {'b': {'d': {'e': [{'x': 1}]}}, 'c': [{'x': 2}], 'j': {'k': [{'x': 3}]}}}

Find matching pairs of dictionaries in a list using Python

In a given list:
unmatched_items_array = [{'c': 45}, {'c': 35}, {'d': 5}, {'a': 3.2}, {'a': 3}]
Find all 'key' pairs and print out and if no pairs found for given dictionary print out that dictionary.
What I managed to write so far sort of works but it keeps testing some items of the list even though they were already tested. Not sure how to fix it.
for i in range(len(unmatched_items_array)):
for j in range(i + 1, len(unmatched_items_array)):
# when keys are the same print matching dictionary pairs
if unmatched_items_array[i].keys() == unmatched_items_array[j].keys():
print(unmatched_items_array[i], unmatched_items_array[j])
break
# when no matching pairs print currently processed dictionary
print(unmatched_items_array[i])
Output:
{'c': 45} {'c': 35}
{'c': 45}
{'c': 35}
{'d': 5}
{'a': 3.2} {'a': 3}
{'a': 3.2}
{'a': 3}
What the output should be:
{'c': 45} {'c': 35}
{'d': 5}
{'a': 3.2} {'a': 3}
What am I doing wrong here?
Using collections.defaultdict
Ex:
from collections import defaultdict
unmatched_items_array = [{'c': 45}, {'c': 35}, {'d': 5}, {'a': 3.2}, {'a': 3}]
result = defaultdict(list)
for i in unmatched_items_array:
key, _ = i.items()[0]
result[key].append(i) #Group by key.
for _, v in result.items(): #print Result.
print(v)
Output:
[{'a': 3.2}, {'a': 3}]
[{'c': 45}, {'c': 35}]
[{'d': 5}]
With itertools.groupby:
from itertools import groupby
unmatched_items_array = [{'d': 5}, {'c': 35}, {'a': 3}, {'a': 3.2}, {'c': 45}]
for v, g in groupby(sorted(unmatched_items_array, key=lambda k: tuple(k.keys())), lambda k: tuple(k.keys())):
print([*g])
Prints:
[{'a': 3}, {'a': 3.2}]
[{'c': 35}, {'c': 45}]
[{'d': 5}]
EDIT: If your items in the list are sorted by keys already, then you can skip the sorted() call:
for v, g in groupby(unmatched_items_array, lambda k: tuple(k.keys()) ):
print([*g])

Categories

Resources