Drawing labels that follow their edges in a Networkx graph

2024/10/12 4:29:06

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

Answer

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.

https://en.xdnf.cn/q/69697.html

Related Q&A

randomly choose 100 documents under a directory

There are about 2000 documents under the directory. I want to randomly select some documents and copy them to a new directory automatically.Some relevant information about generating one document name …

Oauth client initialization in python for tumblr API using Python-oauth2

Im new to Oauth. In the past for twitter applications written in Python i used python-oauth2 library to initialize client like this:consumer = oauth.Consumer(key = CONSUMER_KEY, secret = CONSUMER_SECRE…

Model description in django-admin

Is it possible to put a model description or description on the list display page of a certain model in django-admin?Im talking about something like when you click a model name link on the homepage of…

Print underscore separated integer

Since python3.6, you can use underscore to separate digits of an integer. For examplex = 1_000_000 print(x) #1000000This feature was added to easily read numbers with many digits and I found it very u…

What does (numpy) __array_wrap__ do?

I am diving into the SciPy LinAlg module for the first time, and I saw this function:def _makearray(a):new = asarray(a)wrap = getattr(a, "__array_prepare__", new.__array_wrap__)return new, wr…

SqlAlchemy TIMESTAMP on update extra

I am using SqlAlchemy on python3.4.3 to manage a MySQL database. I was creating a table with:from datetime import datetimefrom sqlalchemy import Column, text, create_engine from sqlalchemy.types import…

Is it possible to pass a dictionary with extraneous elements to a Django object.create method?

I am aware that when using MyModel.objects.create in Django, it is possible to pass in a dictionary with keys which correspond to the model fields in MyModel. This is explained in another question here…

When should I use varargs in designing a Python API?

Is there a good rule of thumb as to when you should prefer varargs function signatures in your API over passing an iterable to a function? ("varargs" being short for "variadic" or …

PyPDF2 wont extract all text from PDF

Im trying to extract text from a PDF (https://www.sec.gov/litigation/admin/2015/34-76574.pdf) using PyPDF2, and the only result Im getting is the following string:bHere is my code:import PyPDF2 import …

Python 3.4 decode bytes

I am trying to write a file in python, and I cant find a way to decode a byte object before writing the file, basically, I am trying to decode this bytes string:Les \xc3\x83\xc2\xa9vad\xc3\x83\xc2\xa9s…