How to create a directed networkx graph from a pan

2019-07-14 20:01发布

问题:

I have a pandas dataframe of the the following form, df,

    A    B    C    D
A   0   0.5   0.5  0 
B   1    0    0    0
C   0.8  0    0   0.2
D   0    0    1    0

I am trying to create a networkx graph from this. I have tried the following variations of code:

A)

G=networkx.from_pandas_adjacency(df)
G=networkx.DiGraph(G) 

B)

G=networkx.from_pandas_adjacency(df, create_using=networkx.DiGraph())

However, what ends up happening is that the graph object either:

(For option A) basically just takes one of the values among the two parallel edges between any two given nodes, and deletes the other one.

(For option B) takes one of the values among the two parallel edges between any two given nodes, as the value for both edges.

For example,

print( list ( filter ( lambda x: x[0]=='A' and x[1] == 'B', list(G.edges.data()) ) ) )

and

print( list ( filter ( lambda x: x[0]=='B' and x[1] == 'A', list(G.edges.data()) ) ) )

prints 1 and [] for option A. prints two 1s for option B.

How do i resolve this issue?

回答1:

Try using numpy as a workaround.

G = nx.from_numpy_matrix(df.values, parallel_edges=True, 
                         create_using=nx.MultiDiGraph())

# Because we use numpy, labels need to be reset
label_mapping = {0: "A", 1: "B", 2: "C", 3: "D"}
G = nx.relabel_nodes(G, label_mapping)

G.edges(data=True)

OutMultiEdgeDataView([('A', 'B', {'weight': 0.5}), 
                      ('A', 'C', {'weight': 0.5}), 
                      ('B', 'A', {'weight': 1.0}), 
                      ('C', 'A', {'weight': 0.8}), 
                      ('C', 'D', {'weight': 0.2}), 
                      ('D', 'C', {'weight': 1.0})])

In a more general case, to get label_mapping you can use

label_mapping = {idx: val for idx, val in enumerate(df.columns)}

This seems to be a bug in networkx 2.0. They will fix it in 2.1. See this issue for more information.