Working with Networkx, I have several edges that need to be displayed in different ways.
For that I use the connectionstyle, some edges are straight lines, some others are Arc3.
The problem is that every edge has a label and the label doesn't follow the edges in these styles.
I borrowed a graph as example :
#!/usr/bin/env python3import networkx as nx
import matplotlib.pyplot as plt# Graph data
names = ['A', 'B', 'C', 'D', 'E']
positions = [(0, 0), (0, 1), (1, 0), (0.5, 0.5), (1, 1)]
edges = [('A', 'B'), ('A', 'C'), ('A', 'D'), ('A', 'E'), ('D', 'A')]# Matplotlib figure
plt.figure('My graph problem')# Create graph
G = nx.MultiDiGraph(format='png', directed=True)for index, name in enumerate(names):G.add_node(name, pos=positions[index])labels = {}
for edge in edges:G.add_edge(edge[0], edge[1])labels[(edge[0], edge[1])] = '{} -> {}'.format(edge[0], edge[1])layout = dict((n, G.node[n]["pos"]) for n in G.nodes())
nx.draw(G, pos=layout, with_labels=True, node_size=300, connectionstyle='Arc3, rad=0.3')nx.draw_networkx_edge_labels(G, layout, edge_labels=labels, connectionstyle='Arc3, rad=0.3')
# Here is the problem : the labels will not follow the edgesplt.show()
That can lead to problems as shows
this example image : we're not sure for which edge is the label.
Is there a way to draw labels that follow their edges ?
Thanks
Yes, it is possible to draw labeled edges of networkx
directed graphs, by using GraphViz. An example using the Python package graphviz
, the Python package networkx
, and the GraphViz program dot
:
"""How to draw a NetworkX graph using GraphViz.Requires:
- The Python package `graphviz`: https://github.com/xflr6/graphviz`pip install graphviz`
- The Python package `networkx`: https://github.com/xflr6/graphviz`pip install networkx`
- The GraphViz program `dot` in the environment's pathhttps://graphviz.org/download/https://en.wikipedia.org/wiki/PATH_(variable)
"""
import graphviz as gv
import networkx as nxdef dump_example_directed_graph():"""Use GraphViz `dot` to layout a directed multigraph.Creates a file named 'example_directed_graph' that containsthe rendered graph."""g = example_directed_graph()h = networkx_to_graphviz(g)filename = 'example_directed_graph'fileformat = 'pdf'h.render(filename, format=fileformat, cleanup=True)# The argument `view=True` can be given to# the method `graphviz.dot.Digraph.render`# to open the rendered file with the# default viewer of the operating systemdef dump_example_undirected_graph():"""Use GraphViz `dot` to layout an undirected multigraph.Creates a file named `example_undirected_graph` that containsthe rendered graph."""g = example_undirected_graph()h = networkx_to_graphviz(g)filename = 'example_undirected_graph'fileformat = 'pdf'h.render(filename, format=fileformat, cleanup=True)def example_directed_graph():"""Return a sample directed graph as `networkx.MultiDiGraph`."""g = nx.MultiDiGraph()g.add_node(1, label='A')g.add_node(2, label='B')g.add_edge(1, 2, label='AB-1')g.add_edge(1, 2, label='AB-2')g.add_edge(2, 1, label='BA')return gdef example_undirected_graph():"""Return a sample undirected graph as `networkx.MultiGraph`."""g = nx.MultiGraph()g.add_node(1, label='A')g.add_node(2, label='B')g.add_edge(1, 2, label='AB-1')g.add_edge(1, 2, label='AB-2')return gdef networkx_to_graphviz(g):"""Convert `networkx` graph `g` to `graphviz.Digraph`.@type g: `networkx.Graph` or `networkx.DiGraph`@rtype: `graphviz.Digraph`"""if g.is_directed():h = gv.Digraph()else:h = gv.Graph()for u, d in g.nodes(data=True):h.node(str(u), label=d['label'])for u, v, d in g.edges(data=True):h.edge(str(u), str(v), label=d['label'])return hif __name__ == '__main__':dump_example_directed_graph()dump_example_undirected_graph()
Documentation of the:
- class
graphviz.dot.Graph
for representing undirected graphs
- class
graphviz.dot.Digraph
for representing directed graphs
- method
graphviz.dot.Digraph.node
for adding an annotated node to a graph
- method
graphviz.dot.Digraph.edge
for adding an annotated edge to a graph
- class
networkx.MultiGraph
for representing undirected graphs
- class
networkx.MultiDiGraph
for representing directed graphs
The above code uses networkx == 2.5.1
, graphviz == 0.16
, and GraphViz version 2.40.1.
Current possibilities using matplotlib
It appears that currently networkx
supports:
- unlabeled curved edges using the function
networkx.drawing.nx_pylab.draw_networkx_edges
with the argument connectionstyle
, or
- labeled straight edges using the function
networkx.drawing.nx_pylab.draw_networkx_edges
with the argument edge_labels
.
So as of networkx <= 2.5.1
, labeled curved edges cannot be drawn with matplotlib
. As a result, for a directed graph with a pair of labeled edges that connect the same nodes (e.g., an edge 1 -> 2
and an edge 2 -> 1
), the edges would be drawn in matplotlib
to overlap, so not all edge labels will be visible.