Accessing nested tupels in dictionary values - python

I'm posting this question after not finding the answer in the many existing issues related to this topic.
I have a dictionary similar to this one:
foo = {'a': [(1, 0.5), (2, 0.3)], 'b': [(3, 0.4), (4, 0.1)]}
and would like to access the items of the nested tupels, in order to create the following dictionary out of it:
foo = {'a': [1, 2], 'b': [3, 4]}
How do I go about doing this, preferably in a pythonic way?

I think these two solutions are easiest to read:
>>> from operator import itemgetter
>>> {k:map(itemgetter(0), foo[k]) for k in foo}
{'a': [1, 2], 'b': [3, 4]}
or import-less:
>>> {k:[x[0] for x in foo[k]] for k in foo}
{'a': [1, 2], 'b': [3, 4]}

what about?
{x: map(lambda z: z[0], y) for x, y in foo.iteritems()}

Using dict comprehension:
In [214]: {key:list(map(lambda x: x[0], foo[key])) for key in foo}
Out[214]: {'a': [1, 2], 'b': [3, 4]}

{k:list(zip(x,y)[0]) for k,(x,y) in foo.iteritems()}
This gives:
{'a': [1, 2], 'b': [3, 4]}

You can do it with defaultdict from collections module:
>>> from collections import defaultdict
>>> d = defaultdict(list)
>>> d = defaultdict(list)
>>> for k in foo:
for v in foo[k]:
d[k].append(v[0])
>>> d
defaultdict(<class 'list'>, {'a': [1, 2], 'b': [3, 4]})
>>> d['a']
[1, 2]

Related

Merging dictionaries not including duplicate values in python

I would like to merge two dictionaries, but if they have the same key, I would only merge non-duplicate values.
The following code works, but I have a question if it's possible to rewrite this when trying to get a union by using | or (**dict1, **dict2)? When I tried using |, my output would be from this dict_merge({ 'A': [1, 2, 3] }, { 'A': [2, 3, 4] }) to this {'A': [2, 3, 4]}
def dict_merge(dict1, dict2):
for key in dict2.keys():
if key in dict1.keys():
d3 = dict1[key] + dict2[key]
d3 = set(d3)
dict1[key] = list(d3)
else:
dict1[key] = dict2[key]
return dict1
dict_merge({ 'A': [1, 2, 3] }, { 'B': [2, 4, 5, 6]})
Output
{ 'A': [1, 2, 3], 'B': [2, 4, 5, 6] }
Giving your two dictionaries names, let's get the union of their keys.
>>> d1 = { 'A': [1, 2, 3] }
>>> d2 = { 'A': [2, 3, 4] }
>>> d1.keys() | d2.keys()
{'A'}
Assuming the lists are really sets based on your code, we can now iterate over the union of the keys in a dictionary comprehension, and union those two sets and turning them back into a list.
>>> {k: list(set(d1.get(k, [])) | set(d2.get(k, []))) for k in d1.keys() | d2.keys()}
{'A': [1, 2, 3, 4]}
If we incorporate some more interesting dictionaries and repeat the same dictionary comprehension:
>>> d1 = {'A': [1,2,3], 'B': [4,5,6]}
>>> d2 = {'B': [5,6,7,8], 'C': [9,10]}
>>> {k: list(set(d1.get(k, [])) | set(d2.get(k, []))) for k in d1.keys() | d2.keys()}
{'C': [9, 10], 'A': [1, 2, 3], 'B': [4, 5, 6, 7, 8]}
Is that the solution you're wanting?
In [61]: my_dict = {}
...: d1 = {'A': [1, 2, 3], 'C': 123}
...: d2 = {'B': [2, 3, 4], 'A': [1, 2, 3]}
...: for i in set(d1).symmetric_difference(set(d2)):
...: my_dict[i] = d1[i] if i in d1 else d2[i]
...: print(my_dict)
Output :
{'B': [2, 3, 4], 'C': 123}

Restructuring the hierarchy of dictionaries in Python?

If I have a nested dictionary in Python, is there any way to restructure it based on keys?
I'm bad at explaining, so I'll give a little example.
d = {'A':{'a':[1,2,3],'b':[3,4,5],'c':[6,7,8]},
'B':{'a':[7,8,9],'b':[4,3,2],'d':[0,0,0]}}
Re-organize like this
newd = {'a':{'A':[1,2,3],'B':[7,8,9]},
'b':{'A':[3,4,5],'B':[4,3,2]},
'c':{'A':[6,7,8]},
'd':{'B':[0,0,0]}}
Given some function with inputs like
def mysteryfunc(olddict,newkeyorder):
????
mysteryfunc(d,[1,0])
Where the [1,0] list passed means to put the dictionaries 2nd level of keys in the first level and the first level in the 2nd level. Obviously the values need to be associated with their unique key values.
Edit:
Looking for an answer that covers the general case, with arbitrary unknown nested dictionary depth.
Input:
d = {'A':{'a':[1,2,3],'b':[3,4,5],'c':[6,7,8]},
'B':{'a':[7,8,9],'b':[4,3,2],'d':[0,0,0]}}
inner_dict={}
for k,v in d.items():
print(k)
for ka,va in v.items():
val_list=[]
if ka not in inner_dict:
val_dict={}
val_dict[k]=va
inner_dict[ka]=val_dict
else:
val_dict=inner_dict[ka]
val_dict[k]=va
inner_dict[ka]=val_dict
Output:
{'a': {'A': [1, 2, 3], 'B': [7, 8, 9]},
'b': {'A': [3, 4, 5], 'B': [4, 3, 2]},
'c': {'A': [6, 7, 8]},
'd': {'B': [0, 0, 0]}}
you can use 2 for loops, one to iterate over each key, value pair and the second for loop to iterate over the nested dict, at each step form the second for loop iteration you can build your desired output:
from collections import defaultdict
new_dict = defaultdict(dict)
for k0, v0 in d.items():
for k1, v1 in v0.items():
new_dict[k1][k0] = v1
print(dict(new_dict))
output:
{'a': {'A': [1, 2, 3], 'B': [7, 8, 9]},
'b': {'A': [3, 4, 5], 'B': [4, 3, 2]},
'c': {'A': [6, 7, 8]},
'd': {'B': [0, 0, 0]}}
You can use recursion with a generator to handle input of arbitrary depth:
def paths(d, c = []):
for a, b in d.items():
yield from ([((c+[a])[::-1], b)] if not isinstance(b, dict) else paths(b, c+[a]))
from collections import defaultdict
def group(d):
_d = defaultdict(list)
for [a, *b], c in d:
_d[a].append([b, c])
return {a:b[-1][-1] if not b[0][0] else group(b) for a, b in _d.items()}
print(group(list(paths(d))))
Output:
{'a': {'A': [1, 2, 3], 'B': [7, 8, 9]}, 'b': {'A': [3, 4, 5], 'B': [4, 3, 2]}, 'c': {'A': [6, 7, 8]}, 'd': {'B': [0, 0, 0]}}

Python list to dictionnary with indexes

I am trying to convert a list :
[A, B, A, A, B, C]
to a dictionnary with each item and the indexes where it was found :
{ A : [0,2,3], B : [1,4], C : [5] }
Any idea of an efficient way to do that ?
Use a defaultdict and enumerate:
>>> lst = ['a','b','a','a','b','c']
>>> from collections import defaultdict
>>> d = defaultdict(list)
>>> for i, value in enumerate(lst):
... d[value].append(i)
...
>>> d
defaultdict(<class 'list'>, {'a': [0, 2, 3], 'c': [5], 'b': [1, 4]})
Or, this can be accomplished with a plain dict, although, it is usually slower:
>>> lst = ['a','b','a','a','b','c']
>>> d = {}
>>> for i, value in enumerate(lst):
... d.setdefault(value, []).append(i)
...
>>> d
{'a': [0, 2, 3], 'c': [5], 'b': [1, 4]}
You could have, of course, converted the defaultdict to a dict:
>>> d
defaultdict(<class 'list'>, {'a': [0, 2, 3], 'c': [5], 'b': [1, 4]})
>>> dict(d)
{'a': [0, 2, 3], 'c': [5], 'b': [1, 4]}
>>> help(dict)
Try this,
lst = ['A', 'B', 'A', 'A', 'B', 'C']
print {i:[j[0] for j in enumerate(lst) if j[1] == i] for i in set(lst)}
Result
{'A': [0, 2, 3], 'B': [1, 4], 'C': [5]}
Use list comprehension and a dict comprehension. Create a set out of the list first. Then you can easily use enumerate and do this.
>>> l = ["A", "B", "A", "A", "B", "C"]
>>> {i:[j for j,k in enumerate(l) if k==i] for i in set(l)}
{'C': [5], 'B': [1, 4], 'A': [0, 2, 3]}

Summation of elements of dictionary that are list of lists

d = {
'a': [[1, 2, 3], [1, 2, 3]],
'b': [[2, 4, 1], [1, 6, 1]],
}
def add_element(lst):
ad = [sum(i) for i in zip(*lst)]
return ad
def csv_reducer2(dicty):
return {k: list(map(add_element, v)) for k, v in dicty.items()}
csv_reducer2(d)
required output:
{'b': [3, 10, 2], 'a': [2, 4, 6]}
Above is the code I have been trying but it gives an error
zip argument #1 must support iteration
>>> d = {'a': [[1, 2, 3], [1, 2, 3]], 'b': [[2, 4, 1], [1, 6, 1]]}
>>> {k: map(sum, zip(*v)) for k, v in d.items()}
{'a': [2, 4, 6], 'b': [3, 10, 2]}
The following will work on Python 2 or 3:
>>> {k: [a + b for a, b in zip(*v)] for k, v in d.items()}
{'a': [2, 4, 6], 'b': [3, 10, 2]}
The issue with your code is you are mapping add_element to every individual element in v inside your dictionary comprehension. This passes a one-dimensional list to zip in add_element, resulting in your error (since individual integers don't support iteration.
To fix your original code, the only change you need to make is:
return {k: list(map(add_element, v)) for k, v in dicty.items()}
->
return {k: add_element(v) for k, v in dicty.items()}
Because zip(*lst) is trying to transpose multiple rows into columns, but you are only passing it single rows through your original map

How do I build a dict using list comprehension?

How do I build a dict using list comprehension?
I have two lists.
series = [1,2,3,4,5]
categories = ['A', 'B', 'A', 'C','B']
I want to build a dict where the categories are the keys.
Thanks for your answers I'm looking to produce:
{'A' : [1, 3], 'B' : [2, 5], 'C' : [4]}
Because the keys can't exist twice
You have to have a list of tuples. The tuples are key/value pairs. You don't need a comprehension in this case, just zip:
dict(zip(categories, series))
Produces {'A': 3, 'B': 5, 'C': 4} (as pointed out by comments)
Edit: After looking at the keys, note that you can't have duplicate keys in a dictionary. So without further clarifying what you want, I'm not sure what solution you're looking for.
Edit: To get what you want, it's probably easiest to just do a for loop with either setdefault or a defaultdict.
categoriesMap = {}
for k, v in zip(categories, series):
categoriesMap.setdefault(k, []).append(v)
That should produce {'A': [1, 3], 'B': [2, 5], 'C': [3]}
from collectons import defaultdict
series = [1,2,3,4,5]
categories = ['A', 'B', 'A', 'C','B']
result = defaultdict(list)
for key, val in zip(categories, series)
result[key].append(value)
Rather than being clever (I have an itertools solution I'm fond of) there's nothing wrong with a good, old-fashioned for loop:
>>> from collections import defaultdict
>>>
>>> series = [1,2,3,4,5]
>>> categories = ['A', 'B', 'A', 'C','B']
>>>
>>> d = defaultdict(list)
>>> for c,s in zip(categories, series):
... d[c].append(s)
...
>>> d
defaultdict(<type 'list'>, {'A': [1, 3], 'C': [4], 'B': [2, 5]})
This doesn't use a list comprehension because a list comprehension is the wrong way to do it. But since you seem to really want one for some reason: how about:
>> dict([(c0, [s for (c,s) in zip(categories, series) if c == c0]) for c0 in categories])
{'A': [1, 3], 'C': [4], 'B': [2, 5]}
That has not one but two list comprehensions, and is very inefficient to boot.
In principle you can do as Kris suggested: dict(zip(categories, series)), just be aware that there can not be duplicates in categories (as in your sample code).
EDIT :
Now that you've clarified what you intended, this will work as expected:
from collections import defaultdict
d = defaultdict(list)
for k, v in zip(categories, series):
d[k].append(v)
d={ k:[] for k in categories }
map(lambda k,v: d[k].append(v), categories, series )
result:
d is now = {'A': [1, 3], 'C': [4], 'B': [2, 5]}
or (equivalent) using setdefault (thanks Kris R.)
d={}
map(lambda k,v: d.setdefault(k,[]).append(v), categories, series )

Categories

Resources