Derrière ce titre énigmatique, qui n'aura pas été sans vous rappeler la fameuse phrase du Petit Prince d'Antoine de Saint Exupéry, se cache un module pour Python qui dira sûrement quelque chose à nos lecteurs assidus spécialisés dans les graphes : pygraphviz !
Ce module a été créé autour de GraphViz et vous permet ainsi de faire des graphes sous Python en respectant les normes établies par GraphViz et, par conséquent, d'exporter ou d'importer très facilement vos graphes pour vos différents projets. Sans plus attendre, attaquons-nous à quelques exemples d'utilisations du module. Prêts ? Graphez !
Installation de pygraphviz
Dans un premier temps, il vous faut avoir installé GraphViz.
Disponible depuis les dépôts officiels de Python, le module peut être très facilement installé, à l'aide du gestionnaire pypi :
1 |
pip install pygraphviz |
Si vous n'aimez pas pypi, vous pouvez également utiliser easy_install :
1 |
easy_install pygraphviz |
Vous avez bien tous réussi l'installation ? Place à un peu de pratique 🙂 !
pygraphviz par la pratique
Hello world !
Commençons par créer une première ébauche de notre script :
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#!/usr/bin/env #-*- coding : utf‑8 -*- # Hello world avec pygraphviz, oh oui ! import pygraphviz as pgv graph = pgv.AGraph() graph.add_edge("Hello", "world!") graph.add_node("Hi!") print(graph.string()) |
Exécutez votre script et, un graphe sauvage apparaît ! Ce graphe apparaît tel qu'il aurait été écrit dans un fichier au format dot.
1 2 3 4 |
strict graph { Hello – "world!"; "Hi!"; } |
Maintenant que nous pouvons afficher un graphe pour GraphViz, peut-être voulez-vous pouvoir l'exporter ? Rien de plus simple, rajoutez cette simple ligne :
14 15 |
# Enregistrer le graphe dans un fichier pour GraphViz graph.write('hello_world.dot') |
Bon, d'accord, ce n'est pas très visuel, surtout lorsque nous sommes étranger à la logique de GraphViz. Pourquoi ne pas créer une image du graphe 🙂 ? Voici comment procéder :
16 17 18 19 20 21 |
# Créer une image du graphe graph.layout('dot') graph.draw('hello_world.png') graph.close() |
Et un aperçu de l'image :
![Hello world!](https://i0.wp.com/bioinfo-fr.net/wp-content/uploads/2015/06/hello_world.png?resize=211%2C155&ssl=1)
Félicitations, vous avez créé votre premier graphe sous Python !
Le code final si vous voulez vous amuser avec (faites-le, on apprend mieux en testant !) :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#!/usr/bin/env #-*- coding : utf‑8 -*- # Hello world avec pygraphviz, oh oui ! import pygraphviz as pgv graph = pgv.AGraph() graph.add_edge("Hello", "world!") graph.add_node("Hi!") print(graph.string()) # Enregistrer le graphe dans un fichier pour GraphViz graph.write('hello_world.dot') # Créer une image du graphe graph.layout('dot') graph.draw('hello_world.png') graph.close() |
Jouer avec les ontologies
Pour cet exemple, nous allons reproduire ce graphe déjà existant : Skin development
Notre objectif est ici multiple :
- avoir un graphe orienté
- chaque nœud du graphe doit être une boîte
- le nœud principal qui indique le nom de l'ontologie doit être rempli en gris
- certaines arêtes doivent être de couleur bleue
Par chance pygraphviz nous permet de faire des graphes orientés et de modifier les attributs des nœuds et des arêtes. Voici comment j'ai procédé pour reproduire le graphe :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
#!/usr/bin/env python #-*- coding : utf‑8 -*- import pygraphviz as pgv graph = pgv.AGraph(directed=True) # changement de l'attribut shape (forme) pour le type box (boîte) graph.node_attr["shape"] = "box" # le premier noeud doit être rempli (style="filled") et coloré en gris (color='grey') graph.add_node("skin development", style="filled", color='grey') graph.add_edge("skin development", "organ development") graph.add_edge("organ development", "anatomical structural development") # l'arête doit être colorée en bleue (color="blue) graph.add_edge("organ development", "system development", color='blue') graph.add_edge("system development", "anatomical structural development") graph.add_edge("system development", "multicellular organismal development", color="blue") graph.add_edge("anatomical structural development", "developmental process") graph.add_edge("multicellular organismal development", "single-organism developmental process") graph.add_edge("multicellular organismal development", "single-multicellular organism process") graph.add_edge("single-organism developmental process", "developmental process") graph.add_edge("single-organism developmental process", "single-organism process") graph.add_edge("single-multicellular organism process", "single-organism process") graph.add_edge("single-multicellular organism process", "multicellular organismal process") graph.add_edge("developmental process", "biological process") graph.add_edge("single-organism process", "biological process") graph.add_edge("multicellular organismal process", "biological process") graph.layout('dot') graph.draw("ontologies.png") graph.close() |
Ce qui vous donne ce résultat une fois le script exécuté :
![Reproduction des ontologies de QuickGO | par Norore, CC-by 0](https://i0.wp.com/bioinfo-fr.net/wp-content/uploads/2015/06/ontologies.png?resize=300%2C233&ssl=1)
Bon, on a un bon premier aperçu, mais avouez que le code est un peu lourd à taper et à relire. Heureusement, pygraphviz nous permet de créer un graphe à partir d'une liste de nœuds ou d'une liste d'arêtes ! Pour nos arêtes, nous allons utiliser la fonction add_edges_from(list) :
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
list_edges_black = [("skin development", "organ development"), \ ("organ development", "anatomical structural development"), \ ("system development", "anatomical structural development"), \ ("anatomical structural development", "developmental process"), \ ("multicellular organismal development", "single-organism developmental process"), \ ("multicellular organismal development", "single-multicellular organism process"), \ ("single-organism developmental process", "developmental process"), \ ("single-organism developmental process", "single-organism process"), \ ("single-multicellular organism process", "single-organism process"), \ ("single-multicellular organism process", "multicellular organismal process"), \ ("developmental process", "biological process"), \ ("single-organism process", "biological process"), \ ("multicellular organismal process", "biological process")] list_edges_blue = [("organ development", "system development"), \ ("system development", "multicellular organismal development")] graph.add_edges_from(list_edges_black) graph.add_edges_from(list_edges_blue, color='blue') |
Bien, on y voit déjà un peu plus clair, et ce sera plus facile à modifier si besoin ! Cependant, on aimerait bien que le premier nœud ne soit pas en haut mais en bas du graphe. Pour cela, graphviz nous propose l'option adéquate ! Dans notre instanciation de la classe AGraph, nous devons déclarer l'option rankdir en lui indiquant une orientation bottom -> top (par défaut, top -> bottom) :
7 |
graph = pgv.AGraph(directed=True, rankdir="BT") |
Ce qui nous donne le graphe final suivant :
![ontologies](https://i0.wp.com/bioinfo-fr.net/wp-content/uploads/2015/06/ontologies1.png?resize=300%2C233&ssl=1)
Et le script complet :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
#!/usr/bin/env python #-*- coding : utf‑8 -*- import pygraphviz as pgv graph = pgv.AGraph(directed=True, rankdir="BT") graph.node_attr["shape"] = "box" graph.add_node("skin development", style="filled", color='grey') list_edges_black = [("skin development", "organ development"), \ ("organ development", "anatomical structural development"), \ ("system development", "anatomical structural development"), \ ("anatomical structural development", "developmental process"), \ ("multicellular organismal development", "single-organism developmental process"), \ ("multicellular organismal development", "single-multicellular organism process"), \ ("single-organism developmental process", "developmental process"), \ ("single-organism developmental process", "single-organism process"), \ ("single-multicellular organism process", "single-organism process"), \ ("single-multicellular organism process", "multicellular organismal process"), \ ("developmental process", "biological process"), \ ("single-organism process", "biological process"), \ ("multicellular organismal process", "biological process")] list_edges_blue = [("organ development", "system development"), \ ("system development", "multicellular organismal development")] graph.add_edges_from(list_edges_black) graph.add_edges_from(list_edges_blue, color='blue') graph.layout('dot') graph.draw("ontologies.png") graph.close() |
Utiliser un graphe existant avec pygraphviz
pygraphiz utilisant l'environnement GraphViz afin de réaliser ses graphes, vous pouvez bien évidemment importer un graphe existant pour le dessiner.
Prenons un exemple fourni dans la galerie de GraphViz, l'exemple datastruct : http://www.graphviz.org/Gallery/directed/datastruct.gv.txt (clic-droit et "Enregistrer la cible du lien sous" pour récupérer le fichier). Pour dessiner le graphe, il vous suffit de saisir les lignes suivantes :
1 2 3 4 5 6 7 8 9 10 11 12 |
#!/usr/bin/env python #-*- coding : utf‑8 -*- import pygraphviz as pgv graph = pgv.AGraph(file="datastruct.gv.txt") graph.layout('dot') graph.draw("datastruct.png") graph.close() |
Je ne vous présente pas le résultat, étant donné qu'il est exactement le même que dans l'exemple fournit par GraphViz.
Quel intérêt me direz-vous ? Et bien l'intérêt pourrait être de réutiliser un graphe existant afin de modifier les données contenues. Il est en effet possible de jouer sur les nœuds et les arêtes même si elles n'ont pas été, a priori, d'abord définies par votre script. La classe AGraph, en lisant le fichier du graphe, construit à la volée les différents nœuds et arêtes tout en appliquant les différents attributs. Mais elle permet également, grâce à des méthodes dédiées, d'afficher les nœuds et les arêtes générées. Ce qui permet, par conséquent, de pouvoir les modifier à volonté.
Dans notre exemple, je me suis amusée à compter le nombre d'arêtes (méthode edges_iter())et le nombre de nœuds (méthode nodes_iter()), mais également à changer la couleur des arêtes (méthode edge_attr.update(), en rouge) et la couleur du texte des noeuds (méthode node_attr.update(), en violet) :
![Notre graphe datastruct, qu'il est kitch ! |par Norore, CC-by 0](https://i0.wp.com/bioinfo-fr.net/wp-content/uploads/2015/06/datastruct.png?resize=300%2C206&ssl=1)
Et pour ceux qui veulent voir comment j'ai fait, voici le script final :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#!/usr/bin/env python #-*- coding : utf‑8 -*- import pygraphviz as pgv graph = pgv.AGraph(file="datastruct.gv.txt") edges = [ edge for edge in graph.edges_iter() ] print len(edges),"edges in graph" nodes = [ node for node in graph.nodes_iter() ] print len(nodes),"nodes in graph" graph.edge_attr.update(color="red") graph.node_attr.update(fontcolor="purple") graph.layout('dot') graph.draw("datastruct.png") graph.close() |
Le mot de la fin
En conclusion je dirai que pygraphviz est un excellent module pour Python pour ceux qui souhaitent réaliser des graphes avec leurs données. De plus, il est également possible d'utiliser la puissance de Python pour générer les graphes à la volée une fois les données générées, ce qui peut être pratique dans certains cas. L'autre avantage est également la possibilité d'exporter le graphe au format GraphViz, permettant ainsi de partager plus rapidement et facilement un graphe généré, surtout si celui-ci est imposant. Les fichiers d'image pouvant être plus ou moins lourd, rien ne vaut un bon fichier texte pour soulager les serveurs 🙂 !
Merci à ZaZo0o, hedjour, Romain Retureau et Sylvain P. pour leur relecture et leurs conseils.
Laisser un commentaire