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 python3
import 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 edges
plt.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
A DiGraph stores nodes and edges with optional data, or attributes. DiGraphs hold directed edges. Self loops are allowed but multiple (parallel) edges are not. Nodes can be arbitrary (hashable) Python objects with optional key/value attributes.
MultiGraph (data=None, **attr)[source] An undirected graph class that can store multiedges. Multiedges are multiple edges between two nodes. Each edge can hold optional data or attributes. A MultiGraph holds undirected edges.
A subgraph view of the graph. The graph structure cannot be changed but node/edge attributes can and are shared with the original graph. Notes. The graph, edge and node attributes are shared with the original graph.
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 path
https://graphviz.org/download/
https://en.wikipedia.org/wiki/PATH_(variable)
"""
import graphviz as gv
import networkx as nx
def dump_example_directed_graph():
"""Use GraphViz `dot` to layout a directed multigraph.
Creates a file named 'example_directed_graph' that contains
the 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 system
def dump_example_undirected_graph():
"""Use GraphViz `dot` to layout an undirected multigraph.
Creates a file named `example_undirected_graph` that contains
the 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 g
def 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 g
def 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 h
if __name__ == '__main__':
dump_example_directed_graph()
dump_example_undirected_graph()
Documentation of the:
graphviz.dot.Graph
for representing undirected graphsgraphviz.dot.Digraph
for representing directed graphsgraphviz.dot.Digraph.node
for adding an annotated node to a graphgraphviz.dot.Digraph.edge
for adding an annotated edge to a graphnetworkx.MultiGraph
for representing undirected graphsnetworkx.MultiDiGraph
for representing directed graphsThe above code uses networkx == 2.5.1
, graphviz == 0.16
, and GraphViz version 2.40.1.
matplotlib
It appears that currently networkx
supports:
networkx.drawing.nx_pylab.draw_networkx_edges
with the argument connectionstyle
, ornetworkx.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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With