web-dev-qa-db-fra.com

Tracer des graphiques dirigés dans Python d'une manière qui affiche toutes les bords séparément

J'utilise Python pour simuler un processus qui a lieu sur des graphiques dirigés. Je voudrais produire une animation de ce processus.

Le problème que j'ai rencontré est que la plupart Python bibliothèques de visualisation graphique combinent des paires d'arêtes dirigées dans un seul bord. Par exemple, NetworkX ne dessine que deux bords lorsque Affichage du graphique suivant, alors que je souhaiterais afficher chacun des quatre bords séparément:

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

Je voudrais afficher quelque chose comme ça, avec chaque bord parallèle dessiné séparément:

Desired output format; parallel edges are drawn separately

La question arêtes réciproques dans IGraph in r semble traiter du même problème, mais la solution existe pour la bibliothèque R IGraph, et non le Python un.

Existe-t-il un moyen facile de produire ce style d'intrigue à l'aide d'un fichier existant Python Bibliothèque de visualisation graphique? Ce serait un bonus s'il pouvait supporter des multigrans.

Je suis ouvert aux solutions qui invoquent un programme externe pour produire les images. J'aimerais générer une série de cadres d'animation. La solution doit donc être automatisée.

24
Josh Rosen

Les outils graphviz semblent afficher des bords distincts.

Par exemple, donner ceci:

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

  B -> A;
  C -> B;
}

à dot produit:

example graph

La langue d'entrée de GRAPHVIZ est assez simple afin que vous puissiez la générer seul, bien que la recherche de "python graphviz" présente quelques bibliothèques comprenant un graphviz module sur PYPI .

Voici python qui génère le graphe ci-dessus à l'aide du module graphviz:

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(file_name, view=True)
27

Utilisation de NetworkX, une solution de contournement éventuelle qui évite le fichier E/S et utilise le point par pydot pour la mise en page.

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)

pour pourquoi seek(0) est nécessaire, voir Comment créer une image à partir d'une chaîne en python

Si, dans la console Ipython (QT), il s'agit alors d'imprimer en ligne et une approche plus directe est la suivante:

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)
12
Ioannis Filippidis

Peut-être que je suis un peu tardif mais j'ai trouvé une autre solution à votre problème, alors je l'affiche afin que cela puisse être utile si quelqu'un a le même problème. Cela ajoute l'argument ConnectionStyle argument à NX.DRAW:

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, connectionstyle='arc3, rad = 0.1',)

Ici vous voyez le résultat:

The result

2
AMangipinto