Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Plotting directed graphs in Python in a way that show all edges separately

I'm using Python to simulate a process that takes place on directed graphs. I would like to produce an animation of this process.

The problem that I've run into is that most Python graph visualization libraries combine pairs of directed edges into a single edge. For example, NetworkX draws only two edges when displaying the following graph, whereas I would like to display each of the four edges separately:

import networkx as nx
import matplotlib.pyplot as plt 

G = nx.MultiDiGraph()

G.add_edges_from([
    (1, 2),
    (2, 3),
    (3, 2),
    (2, 1),
])

plt.figure(figsize=(8,8))
nx.draw(G)

Output from NetworkX; parallel edges are overlapping, so only two lines are displayed

I would like to display something like this, with each parallel edge drawn separately:

Desired output format; parallel edges are drawn separately

The question R reciprocal edges in igraph in R seems to deal with the same issue, but the solution there is for the R igraph library, not the Python one.

Is there an easy way to produce this style of plot using an existing Python graph visualization library? It would be a bonus if it could support multigraphs.

I'm open to solutions that invoke an external program to produce the images. I'd like to generate a whole series of animation frames, so the solution must be automated.

like image 598
Josh Rosen Avatar asked Apr 30 '12 06:04

Josh Rosen


People also ask

How do you remove edges from a graph in Python?

Write a method named remove_edge that takes an edge and removes all references to it from the graph. So your graph is a dictionary (of dictionaries) and for an edge u, v you have an entry u in the entry v and vice-versa.

How do you make a directed graph in Python?

Add the nodes from any container (a list, dict, set or even the lines from a file or the nodes from another graph). In addition to strings and integers any hashable Python object (except None) can represent a node, e.g. a customized node object, or even another Graph. Edges: G can also be grown by adding edges.

How do you plot all graphs in Python?

Graphs in Python can be plotted by using the Matplotlib library. Matplotlib library is mainly used for graph plotting. You need to install matplotlib before using it to plot graphs. Matplotlib is used to draw a simple line, bargraphs, histograms and piecharts.


3 Answers

The Graphviz tools appear to display distinct edges.

For example, giving this:

digraph G {   A -> B;   A -> B;   A -> B;   B -> C;    B -> A;   C -> B; } 

to dot produces:

example graph

Graphviz's input language is pretty simple so you can generate it on your own, though searching for "python graphviz" does turn up a couple of libraries including a graphviz module on PyPI.

Here's python that generates the above graph using the graphviz module:

from graphviz import Digraph  dot = Digraph() dot.node('A', 'A') dot.node('B', 'B') dot.node('C', 'C') dot.edges(['AB', 'AB', 'AB', 'BC', 'BA', 'CB'])  print(dot.source) dot.render(view=True) 
like image 67
Laurence Gonsalves Avatar answered Sep 22 '22 08:09

Laurence Gonsalves


Using NetworkX, a possible workaround which avoids file I/O and uses dot via pydot for layout is:

import networkx as nx import matplotlib.pyplot as plt import matplotlib.image as mpimg from io import BytesIO  g = nx.dodecahedral_graph() d = nx.drawing.nx_pydot.to_pydot(g)     # `d` is a `pydot` graph object,     # `dot` options can be easily set     # attributes get converted from `networkx`,     # use set methods to control     # `dot` attributes after creation png_str = d.create_png() sio = BytesIO() # file-like string, appropriate for imread below sio.write(png_str) sio.seek(0) img = mpimg.imread(sio) imgplot = plt.imshow(img) plt.show()  # added to make the script wait before exiting 

for why seek(0) is needed, see How to create an image from a string in python

If within the IPython (qt)console, then the above will print inline and a more direct approach is:

import networkx as nx from IPython.display import Image  g = nx.dodecahedral_graph() d = nx.drawing.nx_pydot.to_pydot(g)  png_str = d.create_png() Image(data=png_str) 
like image 27
Ioannis Filippidis Avatar answered Sep 22 '22 08:09

Ioannis Filippidis


Maybe I am a little late but I found another solution to you issue. I am posting it so that it can be helpful if somebody has the same problem.

It is possible to plot directed graphs with networkx using matplotlib in a way that the edges appear separately, by passing the argument connectionstyle to the function networkx.drawing.nx_pylab.draw:

import matplotlib.pyplot as plt
import networkx as nx


# create a directed multi-graph
G = nx.MultiDiGraph()
G.add_edges_from([
    (1, 2),
    (2, 3),
    (3, 2),
    (2, 1),
])
# plot the graph
plt.figure(figsize=(8,8))
nx.draw(G, connectionstyle='arc3, rad = 0.1')
plt.show()  # pause before exiting

Here you see the result:

The result

See also the documentation of matplotlib.patches.ConnectionStyle about the argument connectionstyle.

like image 31
AMangipinto Avatar answered Sep 21 '22 08:09

AMangipinto