Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Drawing labels that follow their edges in a Networkx graph

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

like image 263
Asimonu Avatar asked Mar 12 '20 16:03

Asimonu


People also ask

What is a DiGraph in NetworkX?

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.

What is MultiGraph in NetworkX?

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.

What is Subgraph in NetworkX?

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.


1 Answers

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:

  • 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.

like image 86
Ioannis Filippidis Avatar answered Sep 27 '22 18:09

Ioannis Filippidis