Comprendre la dynamique de Twitter avec R et Gephi : analyse de texte et centralité

Publié: 2022-07-22

Cet article développe et approfondit l'analyse présentée dans le premier volet de notre série d'analyses des réseaux sociaux. Nous utilisons le même ensemble de données Twitter et le même réseau d'interaction construits dans le premier article. Mais cette fois, l'idée est de mieux déduire les principaux acteurs, d'identifier leurs sujets de discussion, et de comprendre comment ces sujets se sont propagés.

Centralité des réseaux sociaux

Pour atteindre nos objectifs, nous devons d'abord introduire le concept de centralité . En science des réseaux, la centralité fait référence aux nœuds qui ont une forte influence sur le réseau. L'influence est un concept ambigu ; il peut être compris de plusieurs façons. Un nœud avec de nombreuses arêtes est-il plus influent qu'un nœud avec moins d'arêtes mais plus « importantes » ? Qu'est-ce qui constitue un avantage important sur un réseau social ?

Pour répondre à ces ambiguïtés, les scientifiques des réseaux ont développé de nombreuses mesures de centralité. Ici, nous discutons de quatre mesures couramment utilisées, bien que de nombreuses autres soient disponibles.

Diplôme

La mesure la plus courante et la plus intuitive est la centralité des degrés. L'idée derrière la centralité des degrés est simple : mesurez l'influence par le degré du nœud. Il peut avoir des variantes si le graphe est orienté ; dans ce cas, vous pouvez mesurer le degré d'entrée et le degré de sortie - le premier est connu comme le score du hub et le second comme le score d'autorité.

Dans le premier volet de cette série, nous avons utilisé l'approche non dirigée. Cette fois, nous nous concentrons sur l'approche indegree. Cela permet une analyse plus précise en mettant l'accent sur les utilisateurs qui sont retweetés par d'autres par rapport aux utilisateurs qui se contentent de retweeter fréquemment.

Vecteur propre

La mesure du vecteur propre s'appuie sur la centralité du degré. Plus les nœuds influents pointent vers un nœud donné, plus son score est élevé. Nous commençons avec une matrice de contiguïté , où les lignes et les colonnes représentent les nœuds, et nous utilisons un 1 ou un 0 pour indiquer si les nœuds correspondants d'une ligne et d'une colonne données sont connectés. Le calcul principal estime les vecteurs propres de la matrice. Le vecteur propre principal contiendra les mesures de centralité que nous voulons, où la position i contiendra le score de centralité du nœud i .

Classement

Le PageRank est la variation de la mesure du vecteur propre au cœur de Google. La méthode exacte utilisée par Google est inconnue, mais l'idée générale est que chaque nœud commence avec un score de 1, puis distribue son score en parts égales à chacun de ses bords. Par exemple, si un nœud a trois arêtes qui s'étendent à partir de lui, il "envoie" un tiers de son score à travers chaque arête. En même temps, le nœud est rendu plus important par les arêtes qui pointent vers lui. Il en résulte un système résoluble de N équations à N inconnues.

Intermédiaire

La quatrième mesure, l'intermédiarité , utilise une approche très différente. Ici, un nœud est dit influent s'il est inclus dans de nombreux chemins courts entre d'autres nœuds. C'est-à-dire qu'il est responsable de la communication avec de nombreux autres nœuds, connectant "des mondes différents".

Par exemple, dans l'analyse des réseaux sociaux, ces types de nœuds pourraient être compris comme les types de personnes qui aident les autres à trouver de nouveaux emplois ou à établir de nouvelles relations - ce sont les portes vers des cercles sociaux jusque-là inconnus.

Lequel dois-je utiliser ?

La mesure de centralité appropriée dépend de l'objectif de votre analyse. Voulez-vous savoir quels utilisateurs sont fréquemment pointés du doigt par d'autres en termes de quantité ? La centralité des degrés serait probablement votre meilleure option. Ou préférez-vous une mesure de centralité qui tient compte de la qualité ? Dans ce cas, le vecteur propre ou le PageRank donneront de meilleurs résultats. Si vous voulez savoir quels utilisateurs fonctionnent le plus efficacement comme ponts entre différentes communautés, l'intermédiarité est votre meilleure option.

Lorsque vous utilisez plusieurs mesures similaires, par exemple vecteur propre et PageRank, vous pouvez toutes les estimer et voir si elles donnent des classements équivalents. Sinon, vous pouvez approfondir votre analyse des différences ou générer une nouvelle mesure en combinant leurs scores.

Une autre approche utilise l'analyse en composantes principales pour estimer quelle mesure vous donne plus d'informations sur l'influence réelle des nœuds sur votre réseau.

Calcul pratique de la centralité

Voyons comment nous pouvons calculer ces mesures en utilisant R et RStudio. (Ils peuvent également être réalisés avec Gephi.)

Tout d'abord, nous devons charger toutes les bibliothèques que nous allons utiliser tout au long de cet article :

 library("plyr") library(igraph) library(tidyverse) library(NLP) library("tm") library(RColorBrewer) library(wordcloud) library(topicmodels) library(SnowballC) library("textmineR")

Ensuite, nous supprimerons les nœuds isolés des données que nous avons utilisées auparavant, car ils ne sont pas utiles à cette analyse. Ensuite, nous utiliserons les fonctions igraph betweenness , centr_eigen , page_rank et degree pour estimer les mesures de centralité. Enfin, nous stockerons les scores sur l'objet igraph et sur une trame de données pour voir quels utilisateurs étaient les plus centraux.

 load("art1_tweets.RData") Isolated = which(degree(net)==0) net_clean = delete.vertices(net, Isolated) cent<-data.frame(bet=betweenness(net_clean),eig=centr_eigen(net_clean)$vector,prank=(page_rank(net_clean)$vector),degr=degree(net_clean, mode="in")) cent <- cbind(account = rownames(cent), cent)

Nous pouvons maintenant vérifier les 10 utilisateurs les plus centraux pour chaque mesure :

Diplôme
 top_n(cent, 10 ,degr)%>% arrange(desc(degr))%>% select(degr)
Vecteur propre
 top_n(cent, 10 ,eig)%>% arrange(desc(eig))%>% select(eig)
Classement
 top_n(cent, 10 ,prank)%>% arrange(desc(prank))%>% select(prank)
Intermédiaire
 top_n(cent, 10 ,bet)%>% arrange(desc(bet))%>% select(bet)

Les résultats:

Diplôme Vecteur propre Classement Intermédiaire
ESPNFC 5892 PSG_inside 1 mundodabola 0,037 vuesdey 77704
Le football troll 5755 ÉquipagesMat19 0,51 AleLiparoti 0,026 EdmundOris 76425
PSG_inside 5194 eh01195991 0,4 PSG_inside 0,017 ba*****lla 63799
ÉquipagesMat19 4344 Mohamed135680 0,37 Roy Nemer 0,016 FranciscoGaius 63081
brfootball 4054 ActuFoot_ 0,34 Le football troll 0,013 Yemihazan 62534
PSG_espanol 3616 marttvall 0,34 ESPNFC 0,01 hashtag2weet 61123
IbaiOut 3258 ESPNFC 0,3 PSG_espanol 0,007 Angela_FCB 60991
ActuFoot_ 3175 brfootball 0,25 InstantFoot 0,007 Zyyon_ 57269
FootyHumour 2976 SaylorLuneArmée 0,22 IbaiOut 0,006 ÉquipagesMat19 53758
mundodabola 2778 JohnsvillPat 0,2 2010MisterChip 0,006 MdeenOlawale 49572

Nous pouvons voir que les trois premières mesures partagent un certain nombre d'utilisateurs, tels que PSG_inside, ESPNFC, CrewsMat19 et TrollFootball. On peut supposer qu'ils ont eu une forte influence sur la discussion. L'intermédiarité a une approche différente pour mesurer la centralité et ne montre donc pas autant de chevauchement avec les autres techniques.

Remarque : les opinions exprimées par les comptes Twitter mentionnés dans cet article ne reflètent pas celles de Toptal ou de l'auteur.

Dans les images suivantes, vous pouvez voir notre graphique de réseau coloré original avec deux superpositions d'étiquettes d'utilisateur. Dans le premier, les nœuds sont mis en évidence par leurs scores PageRank, et dans le second, par leurs scores d'intermédiarité :

Une image montrant un tracé PageRank coloré, avec les 10 meilleurs utilisateurs et leurs réseaux mis en évidence. Les trois plus gros utilisateurs sont PSG_inside, TrollFootball et ESPNFC. ESPNFC est situé à gauche du tracé et coloré en violet, tandis que PSG_inside est placé à droite de celui-ci, coloré en rouge. TrollFootball est situé plus haut et à droite d'eux, entre les utilisateurs de couleur verte, bleue et orange.
La discussion de Messi avec les 10 meilleurs utilisateurs de PageRank a été mise en évidence

Une image montrant un graphique d'intermédiarité coloré, avec les 10 principaux utilisateurs et leurs réseaux étiquetés et mis en surbrillance. Tous les 10 principaux utilisateurs, dont la taille est plus similaire que dans l'image précédente, sont situés dans le coin inférieur gauche de l'image, qui est de couleur violette. Ils sont étroitement regroupés.
La discussion de Messi avec les 10 meilleurs utilisateurs d'intermédiarité mise en évidence

Gephi peut être utilisé pour reproduire ces images. Vous pouvez estimer l'intermédiarité ou les scores PageRank à l'aide du bouton Diamètre du réseau dans le panneau des statistiques. Ensuite, vous pouvez afficher les noms de nœuds à l'aide d'attributs, comme illustré dans le premier épisode de cette série.

Analyse de texte : R et LDA

Nous pouvons également analyser les discussions sur les réseaux sociaux pour identifier ce dont les utilisateurs ont parlé. Il y a plusieurs façons d'aborder cela. Nous ferons de la modélisation thématique par latent Dirichlet Allocation (LDA), une technique d'apprentissage automatique non supervisée qui nous permet d'estimer quel ensemble de mots a tendance à apparaître ensemble. Ensuite, à travers cet ensemble de mots, nous pouvons déduire le sujet en cours de discussion.

La première étape consiste à assainir le texte. Pour cela, nous définissons la fonction suivante :

 # This function normalizes text by removing Twitter-related terms and noisy characters sanitize_text <- function(text) { # Convert to ASCII to remove accented characters: text <- iconv(text, to = "ASCII", sub = " ") # Move to lower case and delete RT word (this is added by Twitter) text <- gsub("rt", " ", tolower(text)) # Delete links and user names: text <- gsub("@\\w+", " ", gsub("http.+ |http.+$", " ", text)) # Delete tabs and punctuation: text <- gsub("[ |\t]{2,}", " ", gsub("[[:punct:]]", " ", text)) text <- gsub("amp", " ", text) # Remove HTML special character # Delete leading and lagging blanks: text <- gsub("^ ", "", gsub(" $", "", text)) text <- gsub(" +", " ", text) # Delete extra spaces return(text) }

Nous devons également supprimer les mots vides, les doublons et les entrées vides. Ensuite, nous devons convertir notre texte en une matrice de termes de document à traiter par LDA.

Dans cet ensemble de données, nous avons des utilisateurs parlant plusieurs langues (anglais, espagnol, français, etc.). LDA fonctionne mieux si nous nous concentrons sur une seule langue. Nous allons l'appliquer sur les utilisateurs de la plus grande communauté détectée dans le premier volet de cette série, composée principalement de comptes avec des utilisateurs anglophones.

 # Detect communities: my.com.fast <-cluster_louvain(as.undirected(simplify(net))) largestCommunities <- order(sizes(my.com.fast), decreasing=TRUE)[1:3] # Save the usernames of the biggest community: community1 <- names(which(membership(my.com.fast) == largestCommunities[1])) # Sanitize the text of the users of the biggest community: text <- unique(sanitize_text(tweets.df[which(tweets.df$screen_name %in% community1),]$text)) text = text[text!=''] # Delete empty entries stopwords_regex = paste(stopwords('es'), collapse = '\\b|\\b') stopwords_regex = paste0('\\b', stopwords_regex, '\\b') # Remove English stopwords: text = stringr::str_replace_all(text, stopwords_regex, '') # Create the document term matrix: dtm <- CreateDtm(text, doc_names = seq(1:length(text)), ngram_window = c(1, 2))

Nombre de sujets et scores de cohérence

Le principal hyperparamètre que nous devons définir dans LDA est le nombre (k) de sujets que nous voulons estimer. Cependant, comment pouvons-nous le savoir à l'avance? Une approche courante consiste à former des modèles LDA sur différentes valeurs de k et à mesurer la cohérence de chacun. Nous ferons cela pour les valeurs k de 3 à 20, car les valeurs en dehors de cette plage ne valent pas la peine d'être vérifiées, d'après mon expérience :

 tf <- TermDocFreq(dtm = dtm) # Remove infrequent words: tf_trimmed = tf$term[ tf$term_freq > 1 & tf$doc_freq < nrow(dtm) / 2 ] # Create a folder to store trained models: model_dir <- paste0("models_", digest::digest(tf_trimmed, algo = "sha1")) if (!dir.exists(model_dir)) dir.create(model_dir) # Define a function to infer LDA topics: train_lda_model <- function(number_of_topics){ filename = file.path(model_dir, paste0(number_of_topics, "_topics.rda")) # Check if the model already exists: if (!file.exists(filename)) { # To get exactly the same output on each run, use a constant seed: set.seed(12345) lda_model = FitLdaModel(dtm = dtm, k = number_of_topics, iterations = 500) lda_model$k = number_of_topics lda_model$coherence = CalcProbCoherence(phi = lda_model$phi, dtm = dtm, M = 5) save(lda_model, file = filename) } else { load(filename) } lda_model } # The number of topics that we are going to infer in each LDA training run: topic_count = seq(3, 20, by = 1) # Train through the TmParallelApply function models = TmParallelApply(X = topic_count, FUN = train_lda_model, export = c("dtm", "model_dir"))

Ensuite, nous traçons graphiquement la valeur de cohérence de chacun :

 coherence_by_topics_quantity = data.frame( topic_number = sapply(models, function(model_instance) nrow(model_instance$phi)), score_coherence = sapply(models, function(model_instance) mean(model_instance$coherence)), stringsAsFactors = FALSE) ggplot(coherence_by_topics_quantity, aes(x = topic_number, y = score_coherence)) + geom_point() + geom_line(group = 1) + ggtitle("Coherence by Topic") + theme_minimal() + scale_x_continuous(breaks = seq(1,20,1)) + ylab("Coherence Score") + xlab("Number of topics")

Une valeur de cohérence élevée montre une meilleure segmentation du texte en thèmes :

Un graphique montrant le score de cohérence pour différents sujets. Le score de cohérence varie d'un peu plus de 0,05 sur six à sept sujets, trois à 12 sujets ayant tous un score inférieur à 0,065. Le score culmine soudainement à environ 0,105 pour 13 sujets. Ensuite, il passe en dessous de 0,06 pour 17 sujets, jusqu'à près de 0,09 pour 19 sujets, et se termine juste au-dessus de 0,07 pour 20 sujets.

Nous atteignons notre score de cohérence maximal avec k = 13, nous allons donc utiliser le modèle LDA formé avec 13 sujets. Grâce à la fonction GetTopTerms, nous pouvons voir les 10 mots principaux pour chaque sujet et estimer la sémantique du sujet à travers eux :

 best_model <- models[which.max(coherence_by_topics_quantity$score_coherence)][[ 1 ]] # Most important terms by topic: best_model$top_terms <- GetTopTerms(phi = best_model$phi, M = 20) top10 <- as.data.frame(best_model$top_terms) top10

Le tableau suivant détaille les cinq sujets les plus importants détectés et les 10 principaux mots qui les illustrent :

t_1 t_2 t_3 t_4 t_5
1 messi messi messi messi messi
2 lionel Instagram ligue est psg
3 lionel_messi Publier gagner il Leo
4 psg million Buts au leo_messi
5 Madrid aime ch verser maintenant
6 réel spo des ions pas compagnie
sept Barcelone chèvre ch_ions avec Virginie
8 Paris psg ucl du ser
9 Real Madrid bar ballon qui jugador
dix mbapp plus gros monde je majeur

Bien que la plupart des utilisateurs de cette communauté soient anglophones, il existe encore un certain nombre de francophones et d'espagnols (t_4 et t_5 dans le tableau). Nous pouvons en déduire que le premier sujet concerne l'équipe précédente de Messi (le FC Barcelone), le deuxième sujet concerne la publication de Messi sur Instagram et le troisième sujet se concentre sur les réalisations de Messi.

Maintenant que nous avons les sujets, nous pouvons prédire lequel d'entre eux a été le plus discuté. Pour ce faire, nous allons d'abord concaténer les tweets des utilisateurs (encore une fois, de la plus grande communauté) :

 tweets.df.com1 = tweets.df[which(tweets.df$screen_name %in% community1),] users_text <- ddply(tweets.df.com1, ~screen_name, summarise, text = paste(text, collapse = " "))

Ensuite, nous assainissons le texte comme précédemment et créons le DTM. Après cela, nous appelons la fonction de predict en utilisant notre modèle LDA et le DTM comme arguments. De plus, nous avons paramétré la méthode sur Gibbs pour améliorer le temps de calcul car nous avons beaucoup de texte à analyser :

 users_text$text <- sanitize_text(users_text$text) # Get rid of duplicates stopwords_regex = paste(stopwords('en'), collapse = '\\b|\\b') stopwords_regex = paste0('\\b', stopwords_regex, '\\b') users_text$text = stringr::str_replace_all(users_text$text, stopwords_regex, '') dtm.users.com1 <- CreateDtm(users_text$text, doc_names = users_text$screen_name, ngram_window = c(1, 2)) com1.users.topics = predict(best_model, dtm.users.com1, method="gibbs", iterations=100)

Maintenant, dans le bloc de données com1.users.topics , nous voyons combien chaque utilisateur a parlé de chaque sujet :

Compte t_1 t_2 t_3 t_4 t_5 […]
___99e 0,02716049 0.86666666 0,00246913 0,00246913 0,00246913
Chef__ 0,05185185 0.84197530 0,00246913 0,00246913 0,00246913
Memphis 0.00327868 0.00327868 0,03606557 0.00327868 0.00327868
___Alex1 0,00952380 0,00952380 0,00952380 0,00952380 0,00952380
[…]

Enfin, avec ces informations, nous pouvons créer un nouvel attribut sur le graphe de nœuds pour définir quel sujet a été le plus parlé par quel utilisateur. Ensuite, nous pouvons créer un nouveau fichier GML pour le visualiser dans Gephi :

 # Get the subgraph of the first community: net.com1 = induced_subgraph(net,community1) # Estimate the topic with the max score for each user: com1.users.maxtopic = cbind(users_text$screen_name, colnames(com1.users.topics)[apply(com1.users.topics, 1, which.max)]) # Order the users topic data frame by the users' order in the graph: com1.users.maxtopic = com1.users.maxtopic[match(V(net.com1)$name, com1.users.maxtopic[,1]),] # Create a new attr of the graph by the topic most discussed by each user: V(net.com1)$topic = com1.users.maxtopic[,2] # Create a new graph: write_graph(simplify(net.com1), "messi_graph_topics.gml", format = "gml") 

Un graphique de nœuds colorés généré à l'aide de Gephi, montrant ESPNFC comme l'utilisateur le mieux classé par centralité PageRank. ESPNFC est situé près du bas de l'image, avec de nombreux nœuds violets regroupés en dessous.
La plus grande communauté de discussion Messi colorée par sujet et avec des utilisateurs mis en évidence par la centralité du PageRank

Une image montrant le pourcentage d'utilisateurs mis en évidence par chaque couleur utilisée dans le graphique, le violet "t 6" étant la couleur la plus utilisée (40,53 % de tous les utilisateurs du graphique), suivi du vert "t 13" à 11.02 % et bleu/cyan « t 10 » à 9,68 %. Un "NA" gris, en avant-dernière position de cette liste de 11, représente 2,25 %.
Étiquettes de sujet et pourcentage d'utilisateurs pour chaque couleur utilisée dans le graphique

Déduire des sujets importants et appliquer la centralité des réseaux sociaux

Dans le premier volet de cette série, nous avons appris à obtenir des données de Twitter, à créer le graphique d'interaction, à le tracer via Gephi et à détecter les communautés et les utilisateurs importants. Dans cet article, nous avons développé cette analyse en démontrant l'utilisation de critères supplémentaires pour détecter les utilisateurs influents. Nous avons également montré comment détecter et déduire ce dont les utilisateurs parlaient et tracer cela dans le réseau.

Dans notre prochain article, nous continuerons d'approfondir cette analyse en montrant comment les utilisateurs peuvent détecter le fléau des réseaux sociaux : les spambots et les trolls.

Lectures complémentaires sur le blog Toptal Engineering :

  • Analyse des réseaux sociaux à l'aide de Power BI et R : un guide de visuels personnalisés