简体   繁体   中英

How to unfold a python dictionary of lists based on key-value “pairs”?

I have an algorithmic problem using a Python3.x dictionary of lists, though perhaps another data structure is more appropriate.

Let's say I have the following Python dictionary:

dict1 = {1:[4, 12, 22], 2:[4, 5, 13, 23], 3:[7, 15, 25]}

The key 1 associate with the value [4, 12, 22] signifies that 1 is "associated with" 4. 1 is also associated with 12, and 1 associated with 22. Also, 2 is associated with 4, 2 is associated with 5, 2 associated with 13, and 1 associated with 23, etc.

My question is, for this small example, how do I "unfold" this dictionary such that each element of the value list encodes this "association"?

That is, the end result should be:

intended_dict = {1:[4, 12, 22], 2:[4, 5, 13, 23], 3:[7, 15, 25], 
                     4:[1, 2], 5:[2], 12:[1], 13:[2], 15:[3], 22:[1], 23:[2], 25:[3]}

because 4 is associated with 1, 4 is associated with 2, 5 is associate with 2, etc.

Is there a method to "unfold" dictionaries like this?

How would this scale to a far larger dictionary with larger lists with millions of integers?

Perhaps another data structure would be more efficient here, especially with far larger lists?

EDIT: Given the size of the actual dictionary I am working with (not the one posted above), the solution should try to be as memory-/performance-efficient as possible.

The following will do:

intended_dict = dict1.copy()
for k, v in dict1.items():
    for i in v:
        intended_dict.setdefault(i, []).append(k)

One way is using collections.defaultdict

from collections import defaultdict
dict1 = {1:[4, 12, 22], 2:[4, 5, 13, 23], 3:[7, 15, 25]}
d_dict = defaultdict(list)

for k,l in dict1.items():
    for v in l:
        d_dict[v].append(k)

intended_dict = {**dict1, **d_dict}
print (intended_dict)
#{1: [4, 12, 22], 2: [4, 5, 13, 23], 3: [7, 15, 25], 4: [1, 2], 12: [1], 22: [1], 5: [2], 13: [2], 23: [2], 7: [3], 15: [3], 25: [3]}

Simple one liner:

newdict={v:[i for i in dict1.keys() if v in dict1[i]] for k,v in dict1.items() for v in v}
print(newdict)

Output:

{4: [1, 2], 12: [1], 22: [1], 5: [2], 13: [2], 23: [2], 7: [3], 15: [3], 25: [3]}

To merge them:

print({**dict1,**newdict})

You're basically trying to store relations. There's a whole field about this -- they are stored in relational databases, which contain tables . In Python it would be more natural to do this as a list of 2-lists -- or, as your relation is symmetrical and order doesn't matter, a list of 2-sets. An even better solution though is pandas which is the canonical package for doing tables in Python.

For the time being here's how to turn your original thing into a pandas object, and then turn that into your fixed thing for including the symmetries.

import pandas as pd

dict1 = {1:[4, 12, 22], 2:[4, 5, 13, 23], 3:[7, 15, 25]}

relations = pd.DataFrame(
    [[key, value] for key, values in dict1.items() for value in values]
)

print(relations)

Out:
   0   1
0  1   4
1  1  12
2  1  22
3  2   4
4  2   5
5  2  13
6  2  23
7  3   7
8  3  15
9  3  25

result = {
    **{key: list(values) for key, values in relations.groupby(0)[1]},
    **{key: list(values) for key, values in relations.groupby(1)[0]}
}

print(result)

Out:
{1: [4, 12, 22],
 2: [4, 5, 13, 23],
 3: [7, 15, 25],
 4: [1, 2],
 5: [2],
 7: [3],
 12: [1],
 13: [2],
 15: [3],
 22: [1],
 23: [2],
 25: [3]}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM