{"word_count": 1357, "character_count": 8679}
Suite aux cours et aux TD donnés à l'ENSAI, j'ai découvert de nouveaux (et agaçants) problèmes liés à l'analyse de réseau avec R. De plus, la nécessité de commenter les résultats m'est clairement apparue. Ce billet vise donc à détailler au maximum les étapes et les résultats produits par le package statnet - un billet consacré à igraph est en cours de préparation. Le script et les fichiers de données sont téléchargeables ici.Rappel : statnet (et igraph) permettent d'analyser des graphes non valués et one-mode. Si statnet permet de mener des modèles type ERGM, igraph est plus simple d'utilisation. Pour l'analyse de réseau biparti avec R, oubliez statnet et igraph (qui proposent pourtant des options bipartite=TRUE) et voyez plutôt du côté du package bipartite qui est, à ma connaissance, le seul à donner des résultats corrects pour ce type de réseau (voir la démonstration dans le script).1. Liens, attributs et isolés : sur le format et l'importation des donnéesLe graphe fictif qui va servir d'exemple tout au long de ce billet représente les interactions autour d'une machine à café dans une petite entreprise : la population est constituée de 7 individus répartis en 2 équipes et de niveaux d'ancienneté différents. Un lien signale qu'une personne a payé un café à une autre. Le graphe est simple : pas de boucle (on ne prend pas en compte les cafés que s'offre une personne) ; pas de liens de nature différente. Il est orienté (une personne offre à une autre) et valué (combien de cafés ont été offerts par une personne à une autre dans la semaine ?).Les sommets sont définis par deux attributs : un attribut quantitatif relatif à l'ancienneté (ANC - taille du sommet), un attribut qualitatif relatif à l'équipe (TEAM - couleur du sommet). Les liens sont définis par une direction et une intensité (INT). La valeur des attributs est donnée dans le tableau ci-dessous.Un rappel important : statnet (comme igraph) ne prend pas en compte de l'intensité des liens pour les calculs. Elle peut juste être utilisée pour la représentation graphique.Premier problème : statnet génère un graphe à partir d'une liste de liens. Or, le sommet g est isolé. Une solution est de créer dans son fichier de départ un lien fictif g-g puis de préciser lors de l'importation qu'il n'y a pas de boucle.L'importation des données se fait en deux temps (liens, attributs des sommets). La transformation en objet network, manipulable par statnet, nécessite de ne prendre en compte que les deux premières colonnes (origine - destination). Taper ensuite le nom du graphe permet d'en connaître les caractéristiques. De façon étrange, alors qu'il n'y a pas de boucle, statnet indique malgré tout 9 liens et non 8. Pourtant, le calcul de la densité (cf infra) donne un résultat correct - seuls les 8 liens réellement présents sont donc pris en compte dans les calculs.library(statnet)#import fichier liens &#60;- read.table("liens.txt", header=TRUE, sep=";") attributs &#60;- read.table("attributs.txt", header=TRUE, sep=";")g &#60;- as.network(as.matrix(liens[,-3]), loops = FALSE)g&#160;Network attributes: vertices = 7 directed = TRUE hyper = FALSE loops = FALSE multiple = FALSE bipartite = FALSE total edges= 9 missing edges= 0 non-missing edges= 9Vertex attribute names: vertex.namesNo edge attributesPour l'instant, les sommets ont un attribut (leur nom) et les liens n'en ont aucun. Les lignes suivantes permettent de transférer les données des data.frame vers l'objet network.g %v% "ANC" &#60;- attributs$ANCg %v% "TEAM" &#60;- as.numeric(attributs$TEAM)g %e% "INT" &#60;- liens$ISi l'on tape à nouveau g, on observe que les attributs sont désormais plus nombreux.&#160;Vertex attribute names: ANC TEAM vertex.namesEdge attribute names: INTIl est sans aucun doute possible de filtrer le graphe selon les attributs des liens et des sommets (par exemple, extraire le graphe des relations entre membres de l'équipe A) mais si l'opération est facile avec igraph, elle ne l'est pas avec statnet (i.e. je n'ai pas trouvé comment faire, si ce n'est en filtrant les données en amont, avant la transformation en graphe...).Pour obtenir la figure ci-dessus, deux options sont possibles : appeler les données des data.frame, appeler les attributs de l'objet network. Le résultat est heureusement le même.#à partir des fichiers de départgplot(g, gmode="digraph", displaylabels=TRUE, vertex.col=nodeColors, &#160;&#160;&#160;&#160;&#160; vertex.cex= log(attributs$ANC), edge.lwd = liens$I*2)#à partir des attributs de l'objet networkgplot(g, gmode="digraph", displaylabels=TRUE, vertex.col=nodeColor, &#160;&#160;&#160;&#160;&#160; vertex.cex= log(g %v% "ANC"), edge.lwd = g %e% "INT")2. Quelques mesures et quelques précisionsLe calcul de la densité (fonction gden) donne comme résultat 0.1904762, ce qui correspond bien à 8 (nombre de liens présents) divisé par 7*6 (nombre de liens possibles) - pour mémoire, dans un graphe simple orienté de n sommets, le nombre maximum de liens est égal à n*(n-1).Le nombre de composantes connexes s'obtient avec la fonction components et donne ici comme résultat 5. Pourquoi 5 ? Par défaut, statnet considère que, dans un graphe orienté, la connexité recherchée est la forte : il doit exister un chemin (une suite de liens orientés) entre toute paire de sommets au sein d'une composante connexe. La composition des 5 composantes connexes est obtenue avec la commande component.dist. Si l'on souhaite les composantes faiblement connexes (au nombre de 3), il suffit de modifier l'option.components(g)&#160; #   5 composantes fortement connexescs &#60;- component.dist(g) cs$membership #  et leur compositioncomponents(g, connected="weak") #  3 composantes faiblement connexescw &#60;- component.dist(g,connected="weak")cw$membership #compositioncw$csize&#160;&#160;&#160;&#160;&#160; #et tailleLa liste des plus chemins - retour de la commande geodist - comprend deux matrices : $counts donne le nombre de plus courts chemins entre paires de sommets (il peut très bien y avoir plusieurs plus courts chemins entre deux sommets) ; $gdist donne la longueur en liens des plus courts chemins. La commande dyad.census donne un résultat facilement interprétable (nombre de liens mutuels, asymétriques et nuls). La commande triad.census donne en sortie le code MAN (mutuel, asymétrique, nul) des triades et le nombre de triades respectives. Dans le graphe fictif étudié, il n'y a aucune triade complète, et donc 0 sous le code 300.Il est déconseillé d'utiliser la commande clique.census : même avec un graphe aussi petit, et sur un ordinateur récent et plutôt puissant, RStudio plante...En ce qui concerne le degré, la commande degree fournit par défaut la somme des degrés entrants et sortants par sommet. Si l'on choisit de normaliser, la somme des degrés des sommets sera égale à 1 (le mode de normalisation est différent dans igraph). Le graphe étant non connexe, la commande closeness donne un résultat égal à zéro pour tous les sommets (igraph donne lui des résultats aberrants). La centralité d'intermédiarité (betweenness) donne ici 0 pour tous les sommets sauf pour b qui obtient 1 : il y a en effet un seul plus court chemin (dba) passant par b. Si l'on ne prend pas en compte la direction des liens, tous les autres sommets restent à zéro et b descend à 0.5. Pourquoi ? Il y a deux plus courts chemins entre d et a (dba et dca), et 1 sur 2 passe par b. Que c reste à zéro est par contre difficilement explicable... (si vous avez une idée, n'hésitez pas à la soumettre).Enfin, et je termine ici pour l'examen détaillé de certaines fonctions de statnet, la mesure de la centralisation du graphe est correcte dans statnet - on obtient bien 0 avec un graphe type cercle et 1 avec un graphe étoilé - mais elle donne des résultats erronés dans igraph...Dernier point qui concerne les graphes bipartis : statnet permet (difficilement) de les importer puis de calculer des indicateurs mais ceux-ci sont faux. Dans l'exemple du script, nous avons 7 liens, 3 événements et 4 acteurs. Si les 4 acteurs participent aux 3 événements, le graphe est complet, la densité de 1 et le nombre de liens égal à 12. Or, la commande gden donne comme résultat 0.33, soit 7*2 / 7*6 - dans un graphe simple non orienté de n sommets, la densité est égale à 2n / n(n-1). Et si vous tentez l'expérience avec igraph, le résultat est le même. Le seul - et très relatif - intérêt est de pouvoir passer ensuite à des matrices acteurs-acteurs ou événements-événements. Cette dernière opération est possible sans ces packages en multipliant la matrice m par sa transposée pour obtenir la matrice acteurs-acteursm %*% t(m)ou en faisant l'opération inverse (matrice événements-événements)t(m) %*% m