How to label edges of a Multigraph in Networkx and matplotlib?
Asked Answered
S

3

5

I have a Undirected Multigraph and I wanna draw the edges with labels, any suggestion? I the follow suggestion, but still no edges labels. Drawing multiple edges between two nodes with networkx by atomh33ls

G=nx.MultiGraph ()
G.add_edge(1,2,weight=7)
G.add_edge(1,2,weight=2)
G.add_edge(1,2,weight=3)
G.add_edge(3,1,weight=2)
G.add_edge(3,2,weight=3)

node_label = nx.get_node_attributes(G,'id')
pos = nx.spring_layout(G)
nx.draw_networkx_nodes(G, pos, label=node_label)
nx.draw_networkx_labels(G, pos, label=node_label)
edge_labels=nx.get_edge_attributes(G,'weight')
ax = plt.gca()
for e in G.edges:
    ax.annotate("",
                xy=pos[e[0]], xycoords='data',
                xytext=pos[e[1]], textcoords='data',
                arrowprops=dict(arrowstyle="-", color="0.5",
                                shrinkA=5, shrinkB=5,
                                patchA=None, patchB=None,
                                connectionstyle="arc3,rad=rrr".replace('rrr',str(0.3*e[2])
                                ),
                                ),
                )
#nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels)
plt.axis('off')
plt.show()

Undirected MultiGraph example

Skuld answered 22/6, 2020 at 10:51 Comment(0)
T
4

The dictionary returned by nx.get_edge_attributes has the structure (source, dest, enum):attr, where the third field just enumerates the occurrences of each edge. This third field is necessary because keys have to be unique in the dictionary. However this will imply that it cannot be used in nx.draw_networkx_edge_labels, because it expects a (source, dest):attr structured dict.

nx.get_edge_attributes(G,'weight')
# {(1, 2, 0): 7, (1, 2, 1): 2, (1, 2, 2): 3, (1, 3, 0): 2, (2, 3, 0): 3}

So really this won't work on MultiGraphs. Something you can do, following the same idea as here, is to label the edges with the weight values, and export the graph to dot with nx.write_dot, which will use those labels on the visualization.

Taiwan answered 22/6, 2020 at 12:11 Comment(0)
S
4

Thanks to @yatu. This is the elegant solution so far for Undirected Multigraph labeled. Please send me more tips to improve the style!

import networkx as nx
import matplotlib.pyplot as plt
from IPython.display import Image

G=nx.MultiGraph ()
G.add_edge(1,2,weight=1)
G.add_edge(1,2,weight=2)
G.add_edge(1,2,weight=3)
G.add_edge(3,1,weight=4)
G.add_edge(3,2,weight=5)
for edge in G.edges(data=True): edge[2]['label'] = edge[2]['weight']
node_label = nx.get_node_attributes(G,'id')
pos = nx.spring_layout(G)
node_label = nx.get_node_attributes(G,'id')
pos = nx.spring_layout(G)
p=nx.drawing.nx_pydot.to_pydot(G)
p.write_png('multi.png')
Image(filename='multi.png')

Solution

Skuld answered 22/6, 2020 at 20:26 Comment(1)
I'll try give it another look when I have timeTaiwan
U
0

You can use split the drawing of an edge in two calls to ax.annotate(...): one draws the lines and the other the edge labels. See the code below.

This solution is far from being perfect: I still have to figure out how to displace the label of the edges depending on the index e[2], but it's a step in the right direction.

Also, self-loop edges are not rendered properly: being pos[e[0]] and pos[e[1]] the same, the label is rendered on the node itself.

Anyway here is the code, hoping someone else might help improve it:

import networkx as nx
import matplotlib.pyplot as plt

G=nx.MultiGraph ()
G.add_edge(1,2,weight=1)
G.add_edge(1,2,weight=2)
G.add_edge(1,2,weight=3)
G.add_edge(3,1,weight=4)
G.add_edge(3,2,weight=5)

nx.draw_networkx_nodes(g, pos=pos, node_size=300, node_color=palette)
nx.draw_networkx_labels(g, pos=pos)

f = 0.3
ax = plt.gca()
for e in g.edges:
    # draw the edge
    init = - f * (len(g[e[0]][e[1]]) - 1) / 2
    ax.annotate(
        "",
        xy=pos[e[0]], xycoords='data',
        xytext=pos[e[1]], textcoords='data',
        arrowprops=dict(
             arrowstyle="-", color="0.0",
             shrinkA=10, shrinkB=10,
             patchA=None, patchB=None,
             connectionstyle="arc3,rad=rrr".replace('rrr',str(init + e[2] * f)),
        ),
    )
    # render the label
    pt = (pos[e[0]] + pos[e[1]]) / 2
    ax.annotate(
        g[e[0]][e[1]][e[2]]["relation"], # <-- I have the type in this attribute
        xy=pt, xycoords='data', 
        xytext=pt, textcoords='offset fontsize',
        arrowprops=dict(
            arrowstyle="-", color="1.0",
            shrinkA=10, shrinkB=10,
            patchA=None, patchB=None,
            connectionstyle="arc3,rad=rrr".replace('rrr',str(-0.3 * e[2])),    
        ),
    )
plt.axis('off')
plt.show()
Unshroud answered 1/2 at 13:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.