Reputation: 15204
I have a dictionary of dictionaries which dialed down a notch or two looks like this:
a = {114907: {114905: 1.4351310915,
114908: 0.84635577943,
114861: 61.490648372},
113820: {113826: 8.6999361654,
113819: 1.1412795216,
111068: 1.1964946282,
117066: 1.5595617822,
113822: 1.1958951003},
114908: {114906: 1.279878388,
114907: 0.77568252572,
114862: 2.5412545474}
}
The operation I wanna perform is as follows:
For every key of a:
{114905: 1.435.., 114908: 0.846.., 114861: 61.490..}
) contains keys that are present as keys on the outermost one as well (in this case 114908
), replace them with the k, v
values from the latter and remove it entirely.The desired output would be this:
b = {(114907, 114908): {114905: 1.4351310915,
114906: 1.279878388,
114862: 2.5412545474,
114861: 61.490648372},
113820: {113826: 8.6999361654,
113819: 1.1412795216,
111068: 1.1964946282,
117066: 1.5595617822,
113822: 1.1958951003}
}
I really hope you got what I am trying to achieve here because this is not even describable.
This is what I have so far but it fails in several points and I am deeply convinced that I am going down the wrong road. Eventually I will get there but it would be the most inefficient thing ever coded.
from copy import deepcopy
temp = deepcopy(a)
for item in temp:
for subitems, values in temp[item].items():
if values < 1.0:
for k, v in temp[subitems].items():
if k != item:
a[item][k] = v
# a[item].pop(subitems)
for i in a:
print(i, a[i])
#114908 {114905: 1.4351310915, 114906: 1.279878388, 114907: 0.77568252572, 114861: 61.490648372, 114862: 2.5412545474}
#114907 {114905: 1.4351310915, 114906: 1.279878388, 114908: 0.84635577943, 114861: 61.490648372, 114862: 2.5412545474}
#113820 {113826: 8.6999361654, 113819: 1.1412795216, 111068: 1.1964946282, 117066: 1.5595617822, 113822: 1.1958951003}
Side question, why does pop
in dictionaries return the value
only and not the key: value
pair?
EDIT
An important detail which might make the thing easier is that another way to look for what has to be modified are the inner dict values. If they are below 1.0 their keys are bound to be keys of the outer dict as well.
Upvotes: 4
Views: 274
Reputation: 103814
In Python3.x, {}.keys()
returns a view. You can use set operations on a dict view.
So your algorithm is somewhat simplified to:
outer=a.keys()
deletions=set()
new_a={}
for k,di in a.items():
c=outer & di.keys()
if c:
c=c.pop()
if (c,k) not in deletions:
deletions.add((k,c))
else:
new_a[k]=di
for t in deletions:
del a[t[0]][t[1]], a[t[1]][t[0]]
new_a[t]=a[t[0]]
new_a[t].update(a[t[1]])
>>> new_a
{113820: {113826: 8.6999361654,
113819: 1.1412795216,
111068: 1.1964946282,
117066: 1.5595617822,
113822: 1.1958951003},
(114908, 114907): {114905: 1.4351310915,
114906: 1.279878388,
114861: 61.490648372,
114862: 2.5412545474}}
The order of the elements in the tuple may vary depending on the order of iteration and the order of the set operations. Both are unordered with dicts. Since the elements may vary, which dict which is used as the update dict is also unordered.
This function also only works with a single intersection; i.e., there are not tuples created with more than 2 elements as keys.
Upvotes: 1
Reputation: 4249
How about this:
import itertools
b ={}
for k1,v1 in a.items():
for k2,v2 in v1.items():
if k2 in a:
a[k2].pop(k1)
a[k1].pop(k2)
dest = dict(itertools.chain(a[k1].items(), a[k2].items())) #python 2.7
b[(k1,k2)] = dest
print b
answer:
{(114908, 114907): {114905: 1.4351310915, 114906: 1.279878388, 114861: 61.490648372, 114862: 2.5412545474}}
Upvotes: 1
Reputation: 1651
This should work
a = {114907: {114905: 1.4351310915,
114908: 0.84635577943,
114861: 61.490648372},
113820: {113826: 8.6999361654,
113819: 1.1412795216,
111068: 1.1964946282,
117066: 1.5595617822,
113822: 1.1958951003},
114908: {114906: 1.279878388,
114907: 0.77568252572,
114862: 2.5412545474}
}
# Lets call the keys leaders and its value is a dict of
# keys ( call them members ) to floats.
# if a member is also a leader, then the two leaders combine.
leaders = set(a.keys())
leaders_to_members = { leader: set(member_dict.keys()) for leader, member_dict in a.items() }
seen_leaders =set()
b = {}
for leader, members in leaders_to_members.items():
if leader in seen_leaders:
continue
members_as_leaders = members.intersection(leaders)
members_as_leaders.add(leader)
v = {}
for member_leader in members_as_leaders:
v.update(a[member_leader])
seen_leaders.update(members_as_leaders)
# if its just one element, you want it as the key directly
b_key = tuple(members_as_leaders) if len(members_as_leaders) > 1 else members_as_leaders.pop()
# as per your output, you've removed the key to float value if it is a leader
b_val = { k: float_val for k, float_val in v.items() if k not in members_as_leaders }
b[b_key] = b_val
print(b)
Output
{113820: {111068: 1.1964946282,
113819: 1.1412795216,
113822: 1.1958951003,
113826: 8.6999361654,
117066: 1.5595617822},
(114907, 114908): {114861: 61.490648372,
114862: 2.5412545474,
114905: 1.4351310915,
114906: 1.279878388}}
The side question: why does pop in dictionaries return the value only and not the key: value pair?
>>> a.pop()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: pop expected at least 1 arguments, got 0
>>> help(a.pop)
"""
Help on built-in function pop:
pop(...) method of builtins.dict instance
D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
If key is not found, d is returned if given, otherwise KeyError is raised
"""
As you can see, pop expects the key, so it can pop the value. Since you are required to give it the key, it doesn't have to return the key back to you.
Upvotes: 1
Reputation: 12515
# for each "primary key"
for primary in a.keys():
# for each "sub-key"
for sub_key in a[primary].keys():
# if the sub-key is also a primary key
if sub_key in a.keys():
# assign to the subkey the value of its corresponding primary key
a[primary][sub_key] = a[sub_key]
Is this what you're looking for, at least for the first part of your question?
Upvotes: 0