Comprendere le dinamiche di Twitter con R e Gephi: analisi e centralità del testo

Pubblicato: 2022-07-22

Questo articolo amplia e approfondisce l'analisi presentata nella prima puntata della nostra serie di analisi sui social network. Utilizziamo lo stesso set di dati Twitter e la stessa rete di interazione costruita nel primo articolo. Tuttavia, questa volta, l'idea è di dedurre meglio gli attori principali, identificare i loro argomenti di discussione e capire come questi argomenti si diffondono.

Centralità dei social network

Per raggiungere i nostri obiettivi, dobbiamo prima introdurre il concetto di centralità . Nella scienza delle reti, la centralità si riferisce ai nodi che hanno una forte influenza sulla rete. L'influenza è un concetto ambiguo; può essere compreso in molti modi. Un nodo con molti bordi è più influente di un nodo con meno bordi ma più "importanti"? Cosa costituisce un vantaggio importante su un social network?

Per affrontare queste ambiguità, gli scienziati della rete hanno sviluppato molte misure di centralità. Qui discutiamo di quattro misure comunemente utilizzate, anche se molte altre sono disponibili.

Livello

La misura più comune e intuitiva è la centralità del grado. L'idea alla base della centralità del grado è semplice: misurare l'influenza in base al grado del nodo. Può avere varianti se il grafico è diretto; in tal caso, puoi misurare l'indegree e l'outdegree: il primo è noto come punteggio hub e il secondo come punteggio di autorità.

Nella prima puntata di questa serie, abbiamo utilizzato l'approccio non orientato. Questa volta, ci concentriamo sull'approccio indegree. Ciò consente un'analisi più accurata enfatizzando gli utenti che vengono ritwittati da altri rispetto agli utenti che si limitano a ritwittare frequentemente.

Autovettore

La misura dell'autovettore si basa sulla centralità del grado. Più i nodi influenti puntano a un dato nodo, maggiore è il suo punteggio. Iniziamo con una matrice di adiacenza , dove righe e colonne rappresentano i nodi, e usiamo un 1 o 0 per indicare se i nodi corrispondenti di una data riga e colonna sono collegati. Il calcolo principale stima gli autovettori della matrice. L'autovettore principale conterrà le misure di centralità desiderate, dove la posizione i conterrà il punteggio di centralità del nodo i .

PageRank

PageRank è la variazione della misura dell'autovettore alla base di Google. Il metodo esatto utilizzato da Google è sconosciuto, ma l'idea generale è che ogni nodo inizia con un punteggio di 1, quindi distribuisce il proprio punteggio in parti uguali su ciascuno dei suoi bordi. Ad esempio, se un nodo ha tre spigoli che si estendono da esso, "invia" un terzo del suo punteggio attraverso ciascun arco. Allo stesso tempo, il nodo è reso più importante dai bordi che lo puntano. Ciò si traduce in un sistema risolvibile di N equazioni con N incognite.

Frammezzo

La quarta misura, Betweenness , utilizza un approccio molto diverso. Qui, si dice che un nodo è influente se è incluso in molti percorsi brevi tra altri nodi. Cioè, è responsabile della comunicazione con molti altri nodi, collegando "mondi diversi".

Ad esempio, nell'analisi dei social network, questi tipi di nodi possono essere intesi come i tipi di persone che aiutano gli altri a trovare un nuovo lavoro o a stabilire nuove connessioni: sono le porte di circoli sociali precedentemente sconosciuti.

Quale dovrei usare?

La misura di centralità appropriata dipende dall'obiettivo dell'analisi. Vuoi sapere quali utenti vengono frequentemente individuati da altri in termini di quantità? La centralità della laurea sarebbe probabilmente la tua migliore opzione. Oppure preferisci una misura di centralità che tenga conto della qualità? In tal caso, autovettore o PageRank produrranno risultati migliori. Se vuoi sapere quali utenti funzionano in modo più efficace come ponti tra diverse comunità, la condivisione è la tua migliore opzione.

Quando si utilizzano più misure simili, ad esempio autovettore e PageRank, è possibile stimarle tutte e vedere se producono classifiche equivalenti. In caso contrario, puoi approfondire la tua analisi delle differenze o generare una nuova misura combinando i loro punteggi.

Un altro approccio utilizza l'analisi delle componenti principali per stimare quale misura fornisce maggiori informazioni sulla reale influenza dei nodi sulla rete.

Calcolo pratico della centralità

Vediamo come possiamo calcolare queste misure usando R e RStudio. (Si possono fare anche con Gephi.)

Innanzitutto, dobbiamo caricare tutte le librerie che utilizzeremo in questo articolo:

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

Successivamente, rimuoveremo i nodi isolati dai dati che abbiamo utilizzato in precedenza, poiché non sono utili per questa analisi. Quindi, utilizzeremo le funzioni igraph betweenness , centr_eigen , page_rank e degree per stimare le misure di centralità. Infine, memorizzeremo i punteggi sull'oggetto igraph e su un data frame per vedere quali utenti sono stati i più centrali.

 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)

Ora possiamo controllare i 10 utenti più centrali per ogni misura:

Livello
 top_n(cent, 10 ,degr)%>% arrange(desc(degr))%>% select(degr)
Autovettore
 top_n(cent, 10 ,eig)%>% arrange(desc(eig))%>% select(eig)
PageRank
 top_n(cent, 10 ,prank)%>% arrange(desc(prank))%>% select(prank)
Frammezzo
 top_n(cent, 10 ,bet)%>% arrange(desc(bet))%>% select(bet)

I risultati:

Livello Autovettore PageRank Frammezzo
ESPNFC 5892 PSG_dentro 1 mundodabola 0,037 viewdey 77704
TrollFootball 5755 CrewsMat19 0,51 Ale Liparoti 0,026 Edmund Oris 76425
PSG_dentro 5194 eh01195991 0.4 PSG_dentro 0,017 ba*****lla 63799
CrewsMat19 4344 maometto135680 0,37 Roy Nemer 0,016 Francisco Gaius 63081
brcalcio 4054 ActuFoot_ 0,34 TrollFootball 0,013 Yemihazan 62534
PSG_espanol 3616 marttvall 0,34 ESPNFC 0.01 hashtag2weet 61123
Ibai Out 3258 ESPNFC 0.3 PSG_espanol 0,007 Angela_FCB 60991
ActuFoot_ 3175 brcalcio 0,25 InstantFoot 0,007 Zyyon_ 57269
Footy Humour 2976 Saylor Moon Army 0,22 Ibai Out 0.006 CrewsMat19 53758
mundodabola 2778 Johnsvill Pat 0.2 2010 Mister Chip 0.006 MdeenOlawale 49572

Possiamo vedere che le prime tre misure condividono un numero di utenti, come PSG_inside, ESPNFC, CrewsMat19 e TrollFootball. Possiamo presumere che abbiano avuto una forte influenza sulla discussione. Betweenness ha un approccio diverso alla misurazione della centralità e quindi non mostra tante sovrapposizioni con le altre tecniche.

Nota: le opinioni espresse dagli account Twitter menzionati in questo articolo non riflettono quelle di Toptal o dell'autore.

Nelle immagini seguenti, puoi vedere il nostro grafico di rete colorato originale con due sovrapposizioni di etichette utente. Nel primo, i nodi sono evidenziati dai loro punteggi di PageRank, e nel secondo, dai loro punteggi di interconnessione:

Un'immagine che mostra un grafico PageRank colorato, con i primi 10 utenti e le loro reti evidenziate. I tre maggiori utenti sono PSG_inside, TrollFootball ed ESPNFC. ESPNFC si trova sulla sinistra della trama e colorato di viola, mentre PSG_inside è posizionato alla sua destra, colorato di rosso. TrollFootball si trova più in alto e alla loro destra, tra gli utenti di colore verde, blu e arancione.
Discussione di Messi con i primi 10 utenti PageRank evidenziati

Un'immagine che mostra una trama colorata, con i primi 10 utenti e le loro reti etichettate ed evidenziate. Tutti i primi 10 utenti, che sono di dimensioni più simili rispetto all'immagine precedente, si trovano nell'angolo inferiore sinistro dell'immagine, che è di colore viola. Sono raggruppati strettamente.
Discussione di Messi con i primi 10 utenti di Betweenness evidenziati

Gephi può essere utilizzato per riprodurre queste immagini. Puoi stimare i punteggi di Betweenness o PageRank utilizzando il pulsante Network Diameter nel pannello delle statistiche. Quindi, puoi mostrare i nomi dei nodi usando gli attributi come mostrato nella prima puntata di questa serie.

Analisi del testo: R e LDA

Possiamo anche analizzare le discussioni sui social network per identificare di cosa hanno parlato gli utenti. Ci sono diversi modi per avvicinarsi a questo. Effettueremo la modellazione degli argomenti attraverso l'allocazione di Dirichlet latente (LDA), una tecnica di apprendimento automatico non supervisionata che ci consente di stimare quale insieme di parole tendono ad apparire insieme. Quindi, attraverso quell'insieme di parole, possiamo dedurre l'argomento in discussione.

Il primo passo è sanificare il testo. Per fare ciò, definiamo la seguente funzione:

 # 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) }

Dobbiamo anche rimuovere le parole di arresto, i duplicati e le voci vuote. Successivamente, dobbiamo convertire il nostro testo in una matrice di termini di documento da elaborare da LDA.

In questo set di dati, abbiamo utenti che parlano molte lingue (inglese, spagnolo, francese, ecc.). LDA funziona meglio se ci concentriamo su una singola lingua. Lo applicheremo agli utenti della più grande community rilevata nella prima puntata di questa serie, composta principalmente da account con utenti di lingua inglese.

 # 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))

Conteggio degli argomenti e punteggi di coerenza

L'iperparametro principale che dobbiamo definire in LDA è il numero (k) di argomenti che vogliamo stimare. Tuttavia, come possiamo saperlo in anticipo? Un approccio comune consiste nell'addestrare modelli LDA su diversi k valori e misurare la coerenza di ciascuno. Lo faremo per k valori da 3 a 20, poiché i valori al di fuori di questo intervallo non valgono la pena di controllare, secondo la mia esperienza:

 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"))

Quindi, tracciamo graficamente il valore di coerenza di ciascuno:

 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")

Un valore di coerenza elevato mostra una migliore segmentazione del testo in argomenti:

Un grafico che mostra il punteggio di coerenza per diversi argomenti. Il punteggio di coerenza varia da poco più di 0,05 su sei a sette argomenti, con tre a 12 argomenti che hanno tutti un punteggio inferiore a 0,065. Il punteggio raggiunge improvvisamente un picco di circa 0,105 per 13 argomenti. Quindi scende al di sotto di 0,06 per 17 argomenti, fino a quasi 0,09 per 19 argomenti e termina appena sopra 0,07 per 20 argomenti.

Raggiungiamo il nostro punteggio di coerenza di picco con k = 13, quindi utilizzeremo il modello LDA addestrato con 13 argomenti. Attraverso la funzione GetTopTerms, possiamo vedere le 10 parole principali per ogni argomento e stimare la semantica dell'argomento attraverso di esse:

 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

La tabella seguente descrive in dettaglio i cinque argomenti più importanti rilevati e le 10 parole principali che li esemplificano:

t_1 t_2 t_3 t_4 t_5
1 messi messi messi messi messi
2 lionel instagram lega est psg
3 lionel_messi inviare vincita I l Leo
4 psg milioni obiettivi au leo_messi
5 Madrid piace cap versare ora
6 vero spo ioni pas compa
7 Barcellona capra ch_ioni avec va
8 Parigi psg ucl du ser
9 Real Madrid sbarra pallone qui jugador
10 mbapp più grande mondo je maggiore

Sebbene la maggior parte degli utenti di questa comunità parli inglese, ci sono ancora un certo numero di persone che parlano francese e spagnolo (t_4 e t_5 nella tabella). Possiamo dedurre che il primo argomento si riferisce alla squadra precedente di Messi (FC Barcelona), il secondo argomento riguarda il post di Messi su Instagram e il terzo argomento si concentra sui risultati di Messi.

Ora che abbiamo gli argomenti, possiamo prevedere quale di essi è stato il più discusso. Per fare ciò, concateneremo prima i tweet degli utenti (di nuovo, dalla comunità più grande):

 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 = " "))

Quindi, sanifichiamo il testo come prima e creiamo il DTM. Successivamente, chiamiamo la funzione di predict utilizzando il nostro modello LDA e il DTM come argomenti. Inoltre, abbiamo impostato il metodo su Gibbs per migliorare il tempo di calcolo perché abbiamo molto testo da analizzare:

 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)

Ora, nel frame di dati com1.users.topics , vediamo quanto ogni utente ha parlato di ciascun argomento:

Account t_1 t_2 t_3 t_4 t_5 […]
___99° 0.02716049 0.86666666 0.00246913 0.00246913 0.00246913
Capo__ 0.05185185 0.84197530 0.00246913 0.00246913 0.00246913
Menfi 0.00327868 0.00327868 0.03606557 0.00327868 0.00327868
___Alex1 0.00952380 0.00952380 0.00952380 0.00952380 0.00952380
[…]

Infine, con queste informazioni, possiamo creare un nuovo attributo sul grafico del nodo per definire quale argomento è stato maggiormente trattato da quale utente. Quindi possiamo creare un nuovo file GML per visualizzarlo in 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 grafico a nodi colorato generato utilizzando Gephi, che mostra ESPNFC come l'utente con il punteggio più alto in base alla centralità del PageRank. ESPNFC si trova vicino alla parte inferiore dell'immagine, con molti nodi viola raggruppati sotto di essa.
La più grande comunità di discussioni su Messi colorata per argomento e con utenti evidenziati dalla centralità del PageRank

Un'immagine che mostra la percentuale di utenti evidenziata da ciascun colore utilizzato nel grafico, con il viola "t 6" come colore più utilizzato (40,53% di tutti gli utenti nel grafico), seguito dal verde "t 13" a 11,02 % e blu/ciano "t 10" al 9,68%. Una "NA" grigia, nella penultima posizione di questo elenco di 11, rappresenta il 2,25%.
Etichette argomento e percentuale di utenti per ogni colore utilizzato nel grafico

Dedurre argomenti importanti e applicare la centralità dei social network

Nella prima puntata di questa serie, abbiamo imparato come ottenere dati da Twitter, creare il grafico di interazione, tracciarlo tramite Gephi e rilevare comunità e utenti importanti. In questa puntata, abbiamo ampliato questa analisi dimostrando l'uso di criteri aggiuntivi per rilevare utenti influenti. Abbiamo anche dimostrato come rilevare e dedurre ciò di cui stavano parlando gli utenti e tracciarlo nella rete.

Nel prossimo articolo continueremo ad approfondire questa analisi mostrando come gli utenti possono rilevare la piaga dei social media: spambot e troll.

Ulteriori letture sul blog di Toptal Engineering:

  • Analisi dei social network con Power BI e R: una guida agli elementi visivi personalizzati