Avant de commencer, voici un super tutoriel sur les réseaux et leur représentation graphique réalisé par Ognyanova, K. (2021) : Network visualization with R (mini-introduction sur la construction de réseaux, réseaux statiques, réseaux dynamiques, inventaire de packages R à utiliser).
La représentation de réseaux a également été abordée lors du bootcamp Finist’R de 2020.
La représentation peut être statique, en utilisant le package R
igraph
ou encore en passant par le language ggplot
avec ggraph
. Elle peut également être dynamique. L’un des
avantages d’une représentation dynamique est qu’il est possible
d’intéragir avec le réseau, i.e. zoomer, se déplacer, déplacer les
noeuds…etc (pratique pour un premier travail exploratoire par
exemple).
Pour illustrer les différentes fonctionnalités de
visNetwork
, nous allons utiliser un jeu de données sur les
personnages du premier tome de Game of Thrones.
# Import the data set
<- read.csv(file = "https://raw.githubusercontent.com/YunranChen/STA650Lab/master/lab2_igraph2/gotstark_lannister.csv",
got stringsAsFactors = FALSE)[c(1:100),] # take only a reduced set for example
visNetwork
visNetwork
, déployé sur le CRAN, est une librairie de
visualisation de réseaux utilisant vis.js (librairie JavaScript). Une vignette
ainsi qu’un tutoriel sont
disponibles.
Note: Il existe aussi un autre package pour réaliser
des réseaux en 3D, networkD3
, disponible sur le CRAN mais
ce dernier semble ne plus être développé depuis un moment. Les sorties
sont également moins informatives/pratiques à manipuler ou utiliser.
Quelques particularités:
Des objets igraph peuvent être utilisés. La
fonction toVisNetworkData()
permet de convertir les données
igraph en données adaptées aux fonctions de visNetwork. Les attributs
des noeuds (vertices) et des arêtes (edges) sont
conservés et utilisés comme dans igraph. Par exemple, la couleur des
arêtes spécifiée par E(graph)$color
sera réutilisée par
visNetwork pour colorer le graph. (On peut directement représenter le
réseau igraph en visNetwork via visIgraph
.)
Plein d’options de customisation graphique, qui fonctionnent bien
avec shiny
La légende de visNetwork
n’est pas très esthétique..
mais modifiable (avec pas mal d’investissement!)
Attention: Lorsque le nombre de noeuds et/ou d’arêtes est important (e.g. plusieurs centaines d’arêtes), le graph peut mettre beaucoup de temps à s’afficher, voire crasher…
# Load the package
library(visNetwork)
La fonction visNetwork()
prend comme entrées principales
deux data.frames (ou listes):
id
indiquant l’identifiant unique de chaque noeudfrom
et to
, indiquant les noeuds de départ et
d’arrivée de chaque arêteLe reste des variables pouvant être ajoutées aux data.frames correspondent à des options graphiques. Deux possibilités pour la représentation:
visNetwork()
,visEdges()
,
visNodes()
, visGroups()
… etc.# Extract identifier and family name to create got.nodes data.frame
<- unique(c(got$Source, got$Target))
id <- ifelse(grepl(x = id, pattern = "-"),
family sapply(strsplit(id, "-"), "[", 2),
"Unknown") # too many, reduce a bit
!(family%in%c("Stark","Lannister","Baratheon"))] <- "Unknown"
family[
# Create data set of nodes
<- data.frame(id = id, label = id,
got.nodes group = family)
# Create data set of edges
<- data.frame(from = got$Source, to = got$Target,
got.edges weight = got$weight)
visNetwork(nodes = got.nodes, edges = got.edges, main = "Graph basique")
On peut faire apparaître le sens des relations entre les noeuds.
visNetwork(nodes = got.nodes, edges = got.edges, main = "Graph dirigé") %>%
visEdges(arrows = "to") # indicate towards what arrows should point
On peut attribuer une couleur par groupe (si groupe il y a).
visNetwork(nodes = got.nodes, edges = got.edges, main = "Graph avec couleurs et formes customisées") %>%
visGroups(groupname = "Stark", color = "lightseagreen") %>% # only one group can be specified in the function..
visGroups(groupname = "Lannister", color = "#9e1549") %>%
visGroups(groupname = "Baratheon", color = "#3a4991") %>%
visGroups(groupname = "Unknown", color = "#a19d91", shape = "square")
Pour faire apparaître la légende de nos groupes, on utilise la
fonction visLegend()
comme ci-dessous.
visNetwork(nodes = got.nodes, edges = got.edges, main = "Graph avec légende") %>%
visLegend(position = "right", main = "Family") # change the initial place of the legend and add a title
On affiche uniquement les noeuds dont le degré de connexion est égal à un avec noeud donné (celui sur lequel on clique).
visNetwork(nodes = got.nodes, edges = got.edges, main = "Graph surlignant les noeuds et arêtes les plus proches d'un noeud donné") %>%
visOptions(highlightNearest = list(enabled = TRUE, # authorize nodes to be highlighted
degree = 1) # only the ones separated by 1 edge at most
)
On peut sélectionner les noeuds à faire apparaître nettement sur le graphique (les autres seront en nuances de gris en arrière plan) en fonction d’une autre variable que le groupe.
$teams <- c(rep(c("TeamA", "TeamB"), nrow(got.nodes)/2),"TeamA") # create new feature (a random one)
got.nodesvisNetwork(nodes = got.nodes, edges = got.edges, main = "Graph avec sélection sur co-variable") %>%
visOptions(selectedBy = "teams")
Pour faciliter la manipulation du réseau, on peut afficher des boutons de navigation…
visNetwork(nodes = got.nodes, edges = got.edges, main = "Graph avec boutons de navigation") %>%
visInteraction(navigationButtons = TRUE)
… ou de quoi éditer le réseau soit même (ajouter ou supprimer des noeuds, renommer un noeud… etc).
visNetwork(nodes = got.nodes, edges = got.edges, main = "Graph avec bouton d'édition") %>%
visOptions(manipulation = TRUE)
Il est possible de faire apparaître une bulle d’information en
passant au-dessus d’un noeud. Le contenu de la bulle correspond
normalement à l’option title
(ne fonctionne pas très
bien dans l’exemple ici).
visNetwork(nodes = got.nodes, edges = got.edges, main = "Graph avec bulle d'information") %>%
visNodes(title = paste0("Named ",got.nodes$id)) %>%
# to custom the hover boxes, use CSS
visInteraction(tooltipStyle = 'position: fixed;visibility: hidden;padding: 5px;
font-family: verdana;font-size: 14px;font-color: black;background-color: white;
-moz-border-radius: 3px;-webkit-border-radius: 3px;border-radius: 3px;
border: 1px solid #808074;box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.2);
max-width: 200px;word-break: break-all')
A chaque fois que l’on va représenter le graphique, la disposition des noeuds changera, à moins d’utiliser une graine pour fixer l’aléa. Cette représentation peut être optimisée via l’option concernant le layout. Le layout peut être pratique dans certains cas, surtout si l’algorithme de visNetwork ne trouve pas de disposition optimale pour les noeuds. Ces derniers ne vont pas arrêter de bouger, s’entrechoquer, sans arriver à se stabiliser.
visNetwork(nodes = got.nodes, edges = got.edges, main = "Graph avec un layout spécifique (dynamique)") %>%
visLayout(randomSeed = 220826, improvedLayout = TRUE)
visNetwork(nodes = got.nodes, edges = got.edges, main = "Graph avec un layout spécifique (fixe)") %>%
visIgraphLayout(randomSeed = 220826) # layout-nicely from igraph chosen by default
On peut finalement figer le réseau, pour conserver la disposition des noeuds et des arêtes calculée par une fonction layout spécifique par exemple. L’utilisateur ne pourra bouger aucun des éléments du graph.
visNetwork(nodes = got.nodes, edges = got.edges, main = "Graph figé") %>%
visLayout(randomSeed = 220826, improvedLayout = TRUE) %>%
visInteraction(dragNodes = FALSE, dragView = FALSE, zoomView = FALSE)
En plus d’utiliser un layout d’igraph
spécifique pour
diminuer le temps que peut mettre le réseau à être représenté (via
visIgraphLayout()
utilisé ci-dessus), on peut jouer
sur:
visNetwork(nodes = got.nodes, edges = got.edges, main = "Graph avec flèches rectilignes") %>%
visEdges(smooth = FALSE)
vis.js
calcule les
coordonnées des noeuds de manière dynamique et attend que ce calcul soit
stable avant d’afficher le réseau. On peut désactiver cela avec:visNetwork(nodes = got.nodes, edges = got.edges, main = "Graph en cours de stabilisation") %>%
visPhysics(stabilization = FALSE) # not waiting for coordinates to be stable to plot the network
D’autres fonctions spécifiques:
visSave()
: pour exporter le réseau dans un fichier
.htmlvisCollapse()
: pour avoir une représentation groupe à
groupe, plutôt que noeud à noeud (tout les noeuds sont regroupés en un
seul)visNetworkEditor()
: pour customiser le réseau via une
application shiny dédiée (clique-bouton)visSetOptions()
: pour tout customiser (précise que
l’option est à utiliser à ses risques et périls)Plein d’autres petites fonctions utiles pour l’intégration des
réseaux dans Shiny, telles renderVisNetwork()
(partie
server), visNetworkOutput()
(partie ui)
ou encore visExport()
pour exporter le réseau en .png via
un bouton dans l’application.
Une fonction visTree()
pour représenter des arbres
(régression, classification) générés avec rpart
.
Enfin, des fonctions de type clustering, telle
visClusteringByGroup()
(ressemble fortement à
visCollapse()
, statut expérimental).
On peut utiliser l’objet igraph directement dans une fonction “vis-”.
Par défaut, le graph restera figé dans la disposition (le
layout) définie par igraph
. Seul le noeud
sélectionné pourra être déplacé, les autres ne bougeront pas.
library(igraph)
##
## Attaching package: 'igraph'
## The following objects are masked from 'package:future':
##
## %->%, %<-%
## The following objects are masked from 'package:dplyr':
##
## as_data_frame, groups, union
## The following objects are masked from 'package:purrr':
##
## compose, simplify
## The following object is masked from 'package:tidyr':
##
## crossing
## The following object is masked from 'package:tibble':
##
## as_data_frame
## The following objects are masked from 'package:stats':
##
## decompose, spectrum
## The following object is masked from 'package:base':
##
## union
<- graph_from_data_frame(d = got[,-1], vertices = cbind(id, family), directed = FALSE)
g V(g)$color <- "lightseagreen"
visIgraph(g)
On peut aussi convertir l’objet igraph en objet visNetwork.
<- toVisNetworkData(g)
g.vis visNetwork(nodes = g.vis$nodes, edges = g.vis$edges) %>%
visNodes(color = "#9e1549")
Remarque: L’option color de la fonction
visNodes()
n’est pas prise en compte car un argument
couleur existe déjà dans le jeu de données. Dans ce cas, il
faut modifier l’argument au sein même du data.frame.
<- toVisNetworkData(g)
g.vis $nodes$color <- "#9e1549"
g.visvisNetwork(nodes = g.vis$nodes, edges = g.vis$edges)
https://cran.r-project.org/web/packages/visNetwork/vignettes/Introduction-to-visNetwork.html
(vignette de visNetwork
)
http://datastorm-open.github.io/visNetwork/ (tutoriel
d’utilisation de visNetwork
)
https://visjs.github.io/vis-network/examples/ (illustrations des fonctionnalités de vis.js)
https://kateto.net/network-visualization (tutoriel complet et détaillé sur la visualisation de réseaux avec R)
https://github.com/YunranChen/STA650Lab (jeu de données sur Game of Thrones)