Switch key and value in a dictionary of sets - python

I have dictionary something like:
d1 = {'0': {'a'}, '1': {'b'}, '2': {'c', 'd'}, '3': {'E','F','G'}}
and I want result like this
d2 = {'a': '0', 'b': '1', 'c': '2', 'd': '2', 'E': '3', 'F': '3', 'G': '3'}
so I tried
d2 = dict ((v, k) for k, v in d1.items())
but value is surrounded by set{}, so it didn't work well...
is there any way that I can fix it?

You could use a dictionary comprehension:
{v:k for k,vals in d1.items() for v in vals}
# {'a': '0', 'b': '1', 'c': '2', 'd': '2', 'E': '3', 'F': '3', 'G': '3'}
Note that you need an extra level of iteration over the values in each key here to get a flat dictionary.

Another dict comprehension:
>>> {v: k for k in d1 for v in d1[k]}
{'a': '0', 'b': '1', 'c': '2', 'd': '2', 'E': '3', 'F': '3', 'G': '3'}
Benchmark comparison with yatu's:
from timeit import repeat
setup = "d1 = {'0': {'a'}, '1': {'b'}, '2': {'c', 'd'}, '3': {'E','F','G'}}"
yatu = "{v:k for k,vals in d1.items() for v in vals}"
heap = "{v:k for k in d1 for v in d1[k]}"
for _ in range(3):
print('yatu', min(repeat(yatu, setup)))
print('heap', min(repeat(heap, setup)))
print()
Results:
yatu 1.4274586000000227
heap 1.4059823000000051
yatu 1.4562267999999676
heap 1.3701727999999775
yatu 1.4313863999999512
heap 1.3878657000000203
Another benchmark, with a million keys/values:
setup = "d1 = {k: {k+1, k+2} for k in range(0, 10**6, 3)}"
for _ in range(3):
print('yatu', min(repeat(yatu, setup, number=10)))
print('heap', min(repeat(heap, setup, number=10)))
print()
yatu 1.071519999999964
heap 1.1391495000000305
yatu 1.0880677000000105
heap 1.1534022000000732
yatu 1.0944767999999385
heap 1.1526202000000012

Here's another possible solution to the given problem:
def flatten_dictionary(dct):
d = {}
for k, st_values in dct.items():
for v in st_values:
d[v] = k
return d
if __name__ == '__main__':
d1 = {'0': {'a'}, '1': {'b'}, '2': {'c', 'd'}, '3': {'E', 'F', 'G'}}
d2 = flatten_dictionary(d1)
print(d2)

Related

How to find out the keys of a dictionary with maximum number of common values?

Suppose, there is a dictionary like
my_dict = {'A': {'5', '7', '9', '3'},
'B': {'4', '8','3'},
'C': {'5', '3', '2', '9'},
'D': {'1','6', '8','3'},
'E': {'4','3','5'}}
Now the output should be like {A,C} because they have most number of common values.
dct = {'A': {'5', '7', '9', '3'}, 'B': {'4', '8','3'}, 'C': {'5', '3', '2', '9'}, 'D': {'1','6', '8','3'}, 'E': {'4','3','5'}}
ans = [None, None]
mx = 0
for i in dct:
for j in dct:
if i != j and len(dct[i].intersection(dct[j])) > mx:
ans = [i, j]
mx = len(dct[i].intersection(dct[j]))
As there are sets contained, to find number of common elements, we have intersection method.
>>> ans
['A', 'C']
Although, its worth noting that this code would always produce a pair. If you want more number of elements, the number of loops is going to increase accordingly.

How to combine dictionaries into a nested dictionary?

I want to combine my 3 dictionaries into 1 nested dictionary. I wrote the following code to do it using 3 nested for loops. But is there any efficient way or recursive function to the same thing?
X = {"X1":["O","E","P"],"X2":["M"]}
Y = {"O":["a"],"E":["b","c"],"P":["d"],"M":["r"]}
Z = {"a":["1"],"b":["2","3"],"c":[],"d":["4","5"],"r":["6"]}
d1 = {}
for k in X:
A = X[k]
d2 = {}
for v in A:
B = Y[v]
d3 = {}
for i in B:
C = Z[i]
d3.update({i:C})
d2.update({v:d3})
d1.update({k:d2})
You can use simple recursion:
X = {"X1":["O","E","P"],"X2":["M"]}
Y = {"O":["a"],"E":["b","c"],"P":["d"],"M":["r"]}
Z = {"a":["1"],"b":["2","3"],"c":[],"d":["4","5"],"r":["6"]}
start = [X, Y, Z]
def group(d):
return d if all(all(c not in i for i in start) for c in d) else \
{i:group([c[i] for c in start if i in c][0]) for i in d}
r = {a:group(b) for a, b in X.items()}
print(r == d1) #d1 generated from OP's solution
Output:
{'X1': {'O': {'a': ['1']}, 'E': {'b': ['2', '3'], 'c': []}, 'P': {'d': ['4', '5']}}, 'X2': {'M': {'r': ['6']}}}
True
dictionary comprehension for a 1 liner, basically same procedure as your nested for- loop:
{k: {v0:{v1: Z[v1] for v1 in Y[v0]} for v0 in v} for k, v in X.items()}
outputs:
{'X1': {'O': {'a': ['1']},
'E': {'b': ['2', '3'], 'c': []},
'P': {'d': ['4', '5']}},
'X2': {'M': {'r': ['6']}}}
explanation:
OP's algorithm looks up the values list in the next dictionary using as keys each of the values in the current list until the last dictionary is reached. In pseudocode, the nesting looks like:
# pseudo code
for key, values in X
for valX in values:
for valY in Y[valX]: # note Y[valX] is a list
Z[valY]
translating this into a comprehension, we start from the inner-most loop, going out & adding the necessary decoration
step 1:
{y:Z[y] for ys in Y.values() for y in ys}
# out:
{'a': ['1'], 'b': ['2', '3'], 'c': [], 'd': ['4', '5'], 'r': ['6']}
step 2: now we're looking up the ys directly
{x:{y:Z[y] for y in Y[x]} for xs in X.values() for x in xs}
# out:
{'O': {'a': ['1']},
'E': {'b': ['2', '3'], 'c': []},
'P': {'d': ['4', '5']},
'M': {'r': ['6']}}
step 3: now we put in the keys from X & add another layer of dictionary nesting
{k:{x:{y:Z[y] for y in Y[x]} for x in xs} for k, xs in X.items()}
which yields the desired result
In general, when attempting to convert nested-loops to comprehensions, start from the inner most loop, and work outwards.

Append dictionary in a for loop [duplicate]

This question already has answers here:
How to merge dicts, collecting values from matching keys?
(17 answers)
Closed 3 years ago.
I would like to append dictionary in a for loop such that i get a concatenated dictionary. Also, it is not necessary that keys of all dictionary will be exactly same.
For eq
one={'a': '2', 'c': 't', 'b': '4'}
two={'a': '3.4', 'c': '7.6'}
three={'a': 1.2, 'c': 3.4, 'd': '2.3'}
Output:
combined={'a':['2','3.4','1.2'],'b':'4','c':['t','7.6','3.4'],
'd':'2.3'}
Now coming to original question:
Every time a for loop iterates, a dictionary will be generated and i would like to append it.
Something like:
emptydict={}
for x in z:
newdict=x.dict()
emptydict.append(newdict)
print(emptydict)
try this
one={'a': '2', 'c': 't', 'b': '4'}
two={'a': '3.4', 'c': '7.6'}
three={'a': 1.2, 'c': 3.4, 'd': '2.3'}
df = pd.DataFrame([one,two,three])
a b c d
0 2 4 t NaN
1 3.4 NaN 7.6 NaN
2 1.2 NaN 3.4 2.3
df.to_dict(orient='list')
Output
{'a': ['2', '3.4', 1.2],
'b': ['4', nan, nan],
'c': ['t', '7.6', 3.4],
'd': [nan, nan, '2.3']}
You can try something like this.
one = {'a': '2', 'c': 't', 'b': '4'}
two = {'a': '3.4', 'c': '7.6'}
three = {'a': 1.2, 'c': 3.4, 'd': '2.3'}
new_dict = {}
list_dict = [one, two, three]
for d in list_dict:
for key in d:
if key not in new_dict:
new_dict[key] = []
new_dict[key].append(d[key])
print(new_dict)
Output : {'a': ['2', '3.4', 1.2], 'c': ['t', '7.6', 3.4], 'b': ['4'], 'd': ['2.3']}
I have used your examples to do so -
one = {'a': '2', 'c': 't', 'b': '4'}
two = {'a': '3.4', 'c': '7.6'}
three = {'a': 1.2, 'c': 3.4, 'd': '2.3'}
dicts = [one, two, three]
for dictionary in dicts:
for key, value in dictionary.items():
try:
new[key].append(value)
except KeyError:
new[key] = [value]
O/P -
{'a': ['2', '3.4', 1.2], 'c': ['t', '7.6', 3.4], 'b': ['4'], 'd': ['2.3']}
You can try dict-comprehension and list-comprehension :
new_dict = {k : [j[k] for j in [one,two,three] if k in j] for k in set(list(one.keys())+list(two.keys())+list(three.keys())
# Output : { 'a': ['2', '3.4', 1.2], 'b': ['4'], 'c': ['t', '7.6', 3.4], 'd': ['2.3']}
If you want the keys with only one element as possible value not in the list then try this :
new_dict = a = {k : [j[k] for j in [one,two,three] if k in j][0] if len([j[k] for j in [one,two,three] if k in j]) ==1 else [j[k] for j in [one,two,three] if k in j] for k in set(list(one.keys())+list(two.keys())+list(three.keys()))}
# Output : {'a': ['2', '3.4', 1.2], 'b': '4', 'c': ['t', '7.6', 3.4], 'd': '2.3'}

Create a dictionary by zipping together two lists of uneven length [duplicate]

This question already has answers here:
How to zip two differently sized lists, repeating the shorter list?
(15 answers)
Closed 4 years ago.
I have two lists different lengths, L1 and L2. L1 is longer than L2. I would like to get a dictionary with members of L1 as keys and members of L2 as values.
As soon as all the members of L2 are used up. I would like to start over and begin again with L2[0].
L1 = ['A', 'B', 'C', 'D', 'E']
L2 = ['1', '2', '3']
D = dict(zip(L1, L2))
print(D)
As expected, the output is this:
{'A': '1', 'B': '2', 'C': '3'}
What I would like to achieve is the following:
{'A': '1', 'B': '2', 'C': '3', 'D': '1', 'E': '2'}
Use itertools.cycle to cycle around to the beginning of L2:
from itertools import cycle
dict(zip(L1, cycle(L2)))
# {'A': '1', 'B': '2', 'C': '3', 'D': '1', 'E': '2'}
In your case, concatenating L2 with itself also works.
# dict(zip(L1, L2 * 2))
dict(zip(L1, L2 + L2))
# {'A': '1', 'B': '2', 'C': '3', 'D': '1', 'E': '2'}
Use itertools.cycle:
from itertools import cycle
L1 = ['A', 'B', 'C', 'D', 'E']
L2 = ['1', '2', '3']
result = dict(zip(L1, cycle(L2)))
print(result)
Output
{'E': '2', 'B': '2', 'A': '1', 'D': '1', 'C': '3'}
As an alternative you could use enumerate and index L2 modulo the length of L2:
result = {v: L2[i % len(L2)] for i, v in enumerate(L1)}
print(result)
cycle is fine, but I shall add this modulo based approach:
{x: L2[i % len(L2)] for i, x in enumerate(L1)]}
You can also use a collections.deque() to create an circular FIFO queue:
from collections import deque
L1 = ['A', 'B', 'C', 'D', 'E']
L2 = deque(['1', '2', '3'])
result = {}
for letter in L1:
number = L2.popleft()
result[letter] = number
L2.append(number)
print(result)
# {'A': '1', 'B': '2', 'C': '3', 'D': '1', 'E': '2'}
Which pops the left most item currently in L2 and appends it to the end once the number is added to the dictionary.
Note: Both collections.deque.popleft() and collections.deque.append() are O(1) operations, so the above is still O(N), since you need to traverse all the elements in L1.
Other option without dependencies with good old for loop:
D = {}
for i, e in enumerate(L1):
D[e] = L2[i%len(L2)]
D #=> {'A': '1', 'B': '2', 'C': '3', 'D': '1', 'E': '2'}
Or just:
{ e: L2[i%len(L2)] for i, e in enumerate(L1) }
#=> {'A': '1', 'B': '2', 'C': '3', 'D': '1', 'E': '2'}

Find biggest value in a python dict

I have a python dict like below:
{ '1': {'a': '0.6', 'b': '0.8', 'c': '2','d': '0.5'},
'2': {'a': '0.7', 'b': '0.9', 'c': '0.1','d': '0.2'},
'3': {'a': '0.5', 'b': '0.8', 'c': '3'},
}
How could I get the following result?
('2','a','0.7') ('2',b','0.9') ('3','c', '3') ('1','d', '0.5')
Well, here is the code for it (just 5 lines):
total = []
for i in ['a', 'b', 'c', 'd']:
kv = max(a.iterkeys(), key=(lambda key: float(a[key][i]) if i in a[key].keys() else -9.0))
hv = a[kv][i]
total.append((kv, i, hv))
print total
Output:
[('2', 'a', '0.7'), ('2', 'b', '0.9'), ('3', 'c', '3'), ('1', 'd', '0.5')]
-9.0 is just a random low number.
x={ '1': {'a': '0.6', 'b': '0.8', 'c': '2','d': '0.5'},
'2': {'a': '0.7', 'b': '0.9', 'c': '0.1','d': '0.2'},
'3': {'a': '0.5', 'b': '0.8', 'c': '3'},
}
d={}
for i,j in x.iteritems():
for k,m in j.iteritems():
d.setdefault(k,[0,0])
if j[k]>d[k][0]:
d[k]=(j[k],i)
print [(j[1],i,j[0]) for i,j in d.items()]
You can use additional dict to do your job.
Output:[('2', 'a', '0.7'), ('3', 'c', '3'), ('2', 'b', '0.9'), ('1', 'd', '0.5')]
I agree the question is a bit vague.. I recommend you dont use strings as values.. use int or float if you can in the dictionaries, also does not specify if python 2.x or 3.x
but I think you are after something like this..
def filter_dict(values):
result = collections.Counter()
for value in values.keys():
for k, v in values[value].items():
v = float(v)
result[k] = v if v > result[k] else result[k]
return result
this is how it behaves:
class FilterDictTest(unittest.TestCase):
def test_filter_dict(self):
# Arrange
actual = {
'1': {'a': '0.6', 'b': '0.8', 'c': '2', 'd': '0.5'},
'2': {'a': '0.7', 'b': '0.9', 'c': '0.1', 'd': '0.2'},
'3': {'a': '0.5', 'b': '0.8', 'c': '3'}
}
expected = {
'a': 0.7,
'b': 0.9,
'c': 3,
'd': 0.5
}
# Act & Assert
self.assertEquals(filter_dict(actual), expected)
A little late here.
#!/usr/bin/env python3.5
# entry
entry = {'1': {'a': '0.6', 'b': '0.8', 'c': '2','d': '0.5'}, '2': {'a': '0.7', 'b': '0.9', 'c': '0.1','d': '0.2'}, '3': {'a': '0.5', 'b': '0.8', 'c': '3'}}
# identify keys
all_categories = []
for number, dct in entry.items():
for key, val in dct.items():
all_categories = all_categories + list(dct.keys())
all_categories = set(all_categories)
# Get max values
max_values = {category:None for category in all_categories}
for category in all_categories:
for number, dct in entry.items():
if category in dct.keys():
if max_values[category] is None:
max_values[category] = (number, dct[category])
elif float(max_values[category][1]) < float(dct[category]):
max_values[category] = (number, dct[category])
output = [(number, category, value) for (category, (number, value)) in max_values.items()]
print (output)
Output:
[('2', 'a', '0.7'), ('1', 'd', '0.5'), ('2', 'b', '0.9'), ('3', 'c', '3')]
Not exactly in the order you expected them, but the values are correct. It's not the most elegant solution, though.
I iterate a second time in dict to compare values.
values = []
for key in d:
for skey in d[key]:
max = 0
_key_ = ''
for _ in d:
if d[_].has_key(skey):
if d[_][skey]>max:
max = d[_][skey]
_key_ = _
if (_key_, skey, max) not in values:
values.append((_key_, skey, max))
print values

Categories

Resources