64

I managed to produce the graph correctly, but with some more testing noted inconsistent result for the following two different line of codes:

colors = [h.edge[i][j]['color'] for (i,j) in h.edges_iter()]
widths = [h.edge[i][j]['width'] for (i,j) in h.edges_iter()]
nx.draw_circular(h, edge_color=colors, width=widths)

This approach results in consistent output, while the following produces wrong color/size per the orders of edges:

colors = list(nx.get_edge_attributes(h,'color').values())
widths = list(nx.get_edge_attributes(h,'width').values())
nx.draw_circular(h, edge_color=colors, width=widths)

However, it looks to me the above two lines both rely on the function call to return the attributes per the order of edges. Why the different results?

It looks a bit clumsy to me to access attributes with h[][][]; is it possible to access it by dot convention, e.g. edge.color for edge in h.edges().

Or did I miss anything?

yatu
  • 86,083
  • 12
  • 84
  • 139
timeislove
  • 1,075
  • 1
  • 9
  • 14

3 Answers3

92

The order of the edges passed to the drawing functions are important. If you don't specify (using the edges keyword) you'll get the default order of G.edges(). It is safest to explicitly give the parameter like this:

import networkx as nx

G = nx.Graph()
G.add_edge(1,2,color='r',weight=2)
G.add_edge(2,3,color='b',weight=4)
G.add_edge(3,4,color='g',weight=6)

pos = nx.circular_layout(G)

edges = G.edges()
colors = [G[u][v]['color'] for u,v in edges]
weights = [G[u][v]['weight'] for u,v in edges]

nx.draw(G, pos, edges=edges, edge_color=colors, width=weights)

This results in an output like this: enter image description here

Ben
  • 2,314
  • 1
  • 19
  • 36
Aric
  • 24,511
  • 5
  • 78
  • 77
27

Dictionaries are the underlying data structure used for NetworkX graphs, and as of Python 3.7+ they maintain insertion order. This means that we can safely use nx.get_edge_attributes to retrieve edge attributes since we are guaranteed to have the same edge order in every run of Graph.edges() (which is internally called by get_edge_attributes).

So when plotting, we can directly set attributes such as edge_color and width from the result returned by get_edge_attributes. Here's an example:

G = nx.Graph()
G.add_edge(0,1,color='r',weight=2)
G.add_edge(1,2,color='g',weight=4)
G.add_edge(2,3,color='b',weight=6)
G.add_edge(3,4,color='y',weight=3)
G.add_edge(4,0,color='m',weight=1)

colors = nx.get_edge_attributes(G,'color').values()
weights = nx.get_edge_attributes(G,'weight').values()

pos = nx.circular_layout(G)
nx.draw(G, pos, 
        edge_color=colors, 
        width=list(weights),
        with_labels=True,
        node_color='lightgreen')

enter image description here

yatu
  • 86,083
  • 12
  • 84
  • 139
0

if you want to avoid adding edge colors and alphas / width manually, you may also find this function helpful:

def rgb_to_hex(rgb):
    return '#%02x%02x%02x' % rgb

adjacency_matrix = np.array([[0, 0, 0.5], [1, 0, 1], [1, 0.5, 0]]))
n_graphs = 5
fig, axs = plt.subplots(1, len(n_graphs), figsize=(19,2.5)) 

for graph in range(n_graphs):   

    pos = {0: (1, 0.9), 1: (0.9, 1), 2: (1.1, 1)} 

    # draw DAG graph from adjacency matrix 
    gr = nx.from_numpy_matrix(adjacency_matrix, create_using=nx.DiGraph)
    weights = nx.get_edge_attributes(gr, "weight")
  
    # adding nodes 
    all_rows = range(0, adjacency_matrix.shape[0])
    for n in all_rows:
        gr.add_node(n)
    
    # getting edges 
    edges = gr.edges()
      
    # weight and color of edges 
    scaling_factor = 4 # to emphasise differences 
    alphas = [weights[edge] * scaling_factor for edge in edges]
    colors = [rgb_to_hex(tuple(np.repeat(int(255 * (1- 
    weights[edge])),3))) for edge in edges]
    
    # draw graph 
    nx.draw(gr, 
            pos, 
            ax=axs[graph],
            edgecolors='black', 
            node_color='white', 
            node_size=2000, 
            labels={0: "A", 1: "B", 2: "C"},
            font_weight='bold',
            linewidths=2,
            with_labels=True,
            connectionstyle="arc3,rad=0.15",
            edge_color=colors,
            width=alphas)

  
plt.tight_layout()
  • this allows you to get the edge attributes from the weights in the adjacency matrix and then creates edge weights / alphas and colors based on the attributes – Jan-Philipp Fränken Jun 09 '21 at 11:24