Graphs et théorie des réseaux

en langage python

L'essentiel de cette page

Le module networkx (mais aussi igraph) de python permet de réaliser des graphs.

Cette page explique comment réaliser facilement un réseau et mettre en forme la couleur, la taille des disques et leurs labels.

1- Réaliser un premier graph, un premier réseau

1.1- Matrice d'adjacence

Tout parle d'une matrice d'adjacence : une matrice qui va établir la relation entre des objets.

Voici 4 exemples de matrices d'adjacence :

import numpy as np

A = np.array([[1, 1], [1, 1]]) # 2 objets, relation 1-1

A = np.array([[1, 1], [1, 2]]) # 2 objets, relation réciproque plus forte dans un sens

A = np.array([1,1,0,1,1,1,0,1,1]) ; A=A.reshape(3,3) # 3 objets

A = np.array([1,1,0,1,1,1,1,0,0,1,1,1,1,0,1,1]) ; A=A.reshape(4,4) # 4 objets

print(A)

Voici l'exemple à 4 objets :

[[1 1 0 1]

[1 1 1 0]

[0 1 1 1]

[1 0 1 1]]

On voit ici 4 objets que l'on peut appeler A, B, C et D où A interagit avec B, B avec C, C avec D et D avec A.

1.2- Représentation graphique sous networkx

import networkx as nx

import matplotlib.pyplot as plt

G = nx.from_numpy_array(A)

nx.draw(G)

plt.show()

1.3- Représentation graphique sous igraph

Voici un petit exemple d'igraph que je ne développerai pas plus ici (pour l'instant) car il est difficile à installer (compétition avec un autre igraph, packages graphiques nécessaires comme cairo, non utilisation de matplotlib).

import igraph

A = np.array([1,1,0,1,1,1,1,0,0,1,1,1,1,0,1,1]) ; A=A.reshape(4,4) # 4 objets

A = list(A)

g = igraph.Graph.Adjacency(A,mode=igraph.ADJ_UNDIRECTED)

obj = igraph.plot(g,vertex_label_size=15,vertex_size=35,vertex_color='#ffe4c4')

obj.show()

Quelques commandes utiles quand igraph pose des problèmes...

Archives pour igraphpip install pygobjectpip install cairocffipip install PyGTK pygtkpip install pangocairocffi python -m pip install --user pycairo
https://www.lfd.uci.edu/~gohlke/pythonlibs/#pycairo
CD C:\Users\apcma\Google Drive\IUT\LP\02_Dataviz_pythonpip install pycairo-1.18.1-cp37-cp37m-win_amd64.whl pip install C:\pycairo-1.18.1-cp37-cp37m-win_amd64.whl installer visual code C++14

2- Mise en forme des graphs

2.1- Préparer les données qui serviront d'exemple ici

import networkx as nx

import matplotlib.pyplot as plt

# 10 objets (vertices)

A = np.array([[1,1,0,1,0,0,0,0,0,0],

[1,1,1,0,0,0,0,0,0,0],

[0,0,1,1,1,0,0,0,0,0],

[0,1,0,1,2,0,0,0,0,0],

[0,0,1,1,1,0,0,0,0,0],

[0,1,0,1,2,0,0,0,0,0],

[0,0,1,1,1,0,0,0,0,0],

[0,0,1,1,1,0,0,0,0,0],

[0,1,0,1,2,1,0,0,0,0],

[0,1,0,2,1,0,1,0,0,0]]) ; # On notera une relation forte entre D et E.

G = nx.from_numpy_array(A)

2.2- Mettre des labels aux nodes (networkx)

Il faut éditer un dictionnaire pour l'exemple de la partie 2.1.

dic= {0:"Adonis", 1:"Achille", 2:"Ménélas",3 :"Dartagnan",4 :"Paris",5 :"Hélenne",6 :"Zeus",7 :"Hector",8 :"Agamemnon",9 :"Juliette"}

G = nx.relabel_nodes(G, dic) # Nommer les labels

nx.draw(G,with_labels=True,font_size=8)

plt.show()

plt.figure(figsize=(10,6))

nx.draw(G,with_labels=True,font_size=8,

alpha=0.8,node_color="#A86CF3")

plt.show()

Remarque :

On peut aussi ajouter les labels indépendamment (utile pour la partie 3) :

  • nx.draw_networkx_labels(G,position)

2.3- Mettre en forme les sommets / nœuds / points / vortex / vertices

importance = dict(nx.degree(G))

importance = [v*v*v+40 for v in importance.values()]

nx.draw(G,with_labels=True,font_size=8,

node_color="#F9CD45",

node_size = importance)

plt.show()

2.4- Changer l'épaisseur des traits / liens / lignes en fonction de l'interconnexion

plt.figure(figsize=(10,6))

edges,weights = zip(*nx.get_edge_attributes(G,'weight').items())

position = nx.spring_layout(G)

nx.draw(G, position ,

node_color='#E97EF7',

edgelist=edges,

with_labels=True,

edge_color=weights,

width=weights*3,

edge_cmap=plt.cm.Blues, node_size = importance)

plt.show()

Sur ce graph, on voit que les traits varient en épaisseur et couleur selon leur intensité.

Un exemple où l'on fait varier les couleurs des traits sur le même graph : exemple d'un corrélogramme

# Créer une matrice d'adjacence de type matrice de corrélation

import seaborn as sns

iris = sns.load_dataset('iris')

mycor = iris.corr() # Matrice de corrélation non filtrée par les p-values

import numpy as np

mycor2 = np.array(mycor) # Conversion de la matrice pandas en matrice array

labels = list(mycor.columns.values) # Conversion au format list des noms de variables

key_list = list( range(len(labels)))

dic = dict(zip(key_list, labels))

# Définir les couleurs en fonction des valeurs de corrélation (>0 : rouge, <0 : bleu)

import networkx as nx

G = nx.from_numpy_array(mycor2)


edges,weights = zip(*nx.get_edge_attributes(G,'weight').items())


# Coloration conditionnelle

weights = list(weights)

for i in range(len(weights)) :

if weights[i] < 0 :

weights[i] = "red"

else :

weights[i] = "blue"


weights = tuple(weights)


# Tracer le réseau


# Nommer les labels

G = nx.relabel_nodes(G, dic)

import matplotlib.pyplot as plt

plt.figure(figsize=(6,6))

nx.draw(G,with_labels=True,font_size=8, alpha=0.8, node_color="#FFFFD3", edge_color=weights)


plt.show()

  • Si je souhaite colorer différemment mes arêtes non pas en fonction de la valeur de connexion mais en fonction du type de variables interconnectées :

Exemple : relier en orange les variables seulement si elles appartiennent toutes les 2 à la catégorie A

categorieA = ["sepal_length","sepal_width","petal_length"]

# Coloration conditionnelle

weights = list(weights)

import numpy as np

for i in range(len(edges)) :

#if edge[0].isin(categorieA) :

edge = edges[i]

if (np.isin(edge[0], categorieA)) and (np.isin(edge[1], categorieA)) :

weights[i] = 'orange'

else :

weights[i] = 'pink'

weights = tuple(weights)

# puis ré-exéctuer le code d'affichage ci-dessus

Dans cet igraph, les connexions entre petal_width et une autre variable figurent en rose. Par cette approche, je peux colorer disinctement les connexions (type variable de type A avec A, A avec B ou B avec B)

3- Détection et coloration des communautés

La détection de communauté est disponible sous python mais peut être rendu plus performante si on fait appel à certaines fonctions de R.

Voici un exemple qui se fait automatiquement, mais qui ne doit pas être pris en tant qu'aboutissement car la détection des communautés nécessite un paramétrage subtile et une connaissance de la théorie des graphs.

Cet exemple demande le module communauté, installer :

pip install community python-louvain

En reprenant l'exemple donné au début de la partie 02 :

from community import community_louvain

# Identification des communautés

partition = community_louvain.best_partition(G)

G.add_nodes = partition

position = nx.spring_layout(G) # Identifier la structure du graph

plt.figure(figsize=(7, 7))

plt.axis('off') # Pas d'axes

# Les cercles

nx.draw_networkx_nodes(G, position,

width=100,edge_color="black",

cmap=plt.cm.RdYlBu,

node_color=list(partition.values()))

# Les traits

nx.draw_networkx_edges(G, position, alpha=0.3)

# Les labels (optionnel)

nx.draw_networkx_labels(G,position)

plt.show(G)

4- Changer le type de graphe

4.1- Diagramme Chord (Chord diagram)

plt.figure(figsize=(18,18))

nx.draw_circular(G,

node_color='g', # Couleur des sommets

#(node/vertices/ronds) , vert

edge_color='#909090', # Couleur des traits

node_size=900) # Taille des sommets

plt.axis('equal')

plt.show()

Fonctions à explorer (en projet)

nx.get_node_attributes(G,'name') # pour récupérer les attribues d'un graph

nx.to_dict_of_dicts(G, nodelist=dic) # fonction de gestion des dicts à explorer

G.add_nodes_from(["Bob Morane","Achille"]) # Ajouter des nodes