Search code examples
dictionaryjupyter-notebooknestednetworkx

Converting nested dictionary with overlapping keys to NetworkX


So I have a real mess here, as far as I can see. My goal is a directed and weighted network. The main issue I'm facing is that my output that I'm trying to convert to weighted edges is coming from a 'for' loop, and I tend to get either the first or last output of the loop no matter what I do.

It looks something like this:

import networkx as nx
import matplotlib.pyplot as plt
G2 = nx.DiGraph()
compiledDict = {'A': ['A_1', 'A_2', 'A_3', 'A_4', 'A_5', 'A_6'], 'B': ['B_1', 'B_2', 'B_3', 'B_2', 'B_4', 'B_5', 'B_1', 'B_6', 'B_4']

for x in compiledDict:
    dNotFinalAfterAll={}
    forthelasttime=compiledDict.get(x)
    for item in forthelasttime:
        dNotFinalAfterAll[x]={item:forthelasttime.count(item)}
        print(dNotFinalAfterAll)

And a shortened ver of the output is as follows, each line is one output of the for loop. The goal is to have it where {A:{B:C}} is that A is an outgoing node, B is incoming, and C is the weight:

{'B': {'B_1': 2}}
{'B': {'B_2': 2}}
{'B': {'B_3': 1}}
{'B': {'B_2': 2}}

If there's a way to make it so the duplicates like {'B': {'B_2': 2}} only occur once, that would be good, but as long as it works it can be as ugly as necessary. Needed for proof of concept rather than ongoing process. I want to take this and make it a dataframe. It is not happening. The closest I get is putting this in various places:

G2 = nx.DiGraph((k, v, {'weight': weight}) for k, vs in dNotFinalAfterAll.items() for v, weight in vs.items())
for s, t, w in G2.edges(data=True):
    print(s,t,w)
    G2.add_edge(s,t,weight = w)

Which typically outputs this, whether or not I place it in the for loop:

Z Z_3 {'weight': 1}

Solution

  • Assuming you want to build a weighted DiGraph from your input dictionary, you could take advantage of collections.Counter:

    from collections import Counter
    import networkx as nx
    
    compiledDict = {'A': ['A_1', 'A_2', 'A_3', 'A_4', 'A_5', 'A_6'],
                    'B': ['B_1', 'B_2', 'B_3', 'B_2', 'B_4', 'B_5', 'B_1', 'B_6', 'B_4']}
    
    G2 = nx.DiGraph()
    
    for k1, l in compiledDict.items():
        for k2, w in Counter(l).items():
            G2.add_edge(k1, k2, weight=w)
    

    Or, without the intermediate dictionary:

    import networkx as nx
    
    compiledDict = {'A': ['A_1', 'A_2', 'A_3', 'A_4', 'A_5', 'A_6'],
                    'B': ['B_1', 'B_2', 'B_3', 'B_2', 'B_4', 'B_5', 'B_1', 'B_6', 'B_4']}
    
    G2 = nx.DiGraph()
    
    for k1, l in compiledDict.items():
        for k2 in l:
            if  G2.has_edge(k1, k2):
                G2[k1][k2]['weight'] += 1
            else:
                G2.add_edge(k1, k2, weight=1)
    

    Graph with weights as edge labels:

    networkx graph from nested dictionary