Înțelegerea dinamicii Twitter cu R și Gephi: analiza textului și centralitatea

Publicat: 2022-07-22

Acest articol extinde și aprofundează analiza prezentată în prima parte a seriei noastre de analize a rețelelor sociale. Folosim același set de date Twitter și aceeași rețea de interacțiune construită în primul articol. Cu toate acestea, de data aceasta, ideea este de a deduce mai bine actorii principali, de a identifica subiectele lor de discuție și de a înțelege cum se răspândesc aceste subiecte.

Centralitatea rețelei sociale

Pentru a ne atinge obiectivele, mai întâi trebuie să introducem conceptul de centralitate . În știința rețelelor, centralitatea se referă la nodurile care au o influență puternică asupra rețelei. Influența este un concept ambiguu; poate fi înțeles în multe feluri. Este un nod cu multe margini mai influent decât un nod cu margini mai puține, dar mai „importante”? Ce reprezintă un avantaj important pe o rețea de socializare?

Pentru a aborda aceste ambiguități, oamenii de știință din rețea au dezvoltat multe măsuri de centralitate. Aici, discutăm patru măsuri frecvent utilizate, deși multe altele sunt disponibile.

grad

Cea mai comună și intuitivă măsură este gradul de centralitate. Ideea din spatele centralității gradului este simplă: Măsurați influența prin gradul nodului. Poate avea variante dacă graficul este direcționat; în acest caz, puteți măsura indegree și outdegree — primul este cunoscut sub numele de scor central și al doilea ca scor de autoritate.

În prima parte a acestei serii, am folosit abordarea nedirecționată. De data aceasta, ne concentrăm pe abordarea indegree. Acest lucru permite o analiză mai precisă, punând accent pe utilizatorii care sunt retweetate de către alții față de utilizatorii care pur și simplu retweetează frecvent.

Vector propriu

Măsura vectorului propriu se bazează pe gradul de centralitate. Cu cât nodurile influente indică mai mult către un anumit nod, cu atât scorul acestuia este mai mare. Începem cu o matrice de adiacență , în care rândurile și coloanele reprezintă noduri și folosim un 1 sau 0 pentru a indica dacă nodurile corespunzătoare ale unui rând și coloană date sunt conectate. Calculul principal estimează vectorii proprii ai matricei. Vectorul propriu principal va conține măsurile de centralitate pe care le dorim, unde poziția i va deține scorul de centralitate al nodului i .

PageRank

PageRank este variația măsurării vectorului propriu din centrul Google. Metoda exactă pe care o folosește Google este necunoscută, dar ideea generală este că fiecare nod începe cu un scor de 1, apoi își distribuie scorul în părți egale la fiecare dintre marginile sale. De exemplu, dacă un nod are trei margini care se extind de la el, acesta „trimite” o treime din scorul său prin fiecare margine. În același timp, nodul devine mai important de marginile care indică el. Rezultă un sistem rezolvabil de N ecuații cu N necunoscute.

Între mijloc

A patra măsură, interness , folosește o abordare foarte diferită. Aici, se spune că un nod este influent dacă este inclus în multe căi scurte între alte noduri. Adică, este responsabil pentru comunicarea cu multe alte noduri, conectând „lumi diferite”.

De exemplu, în analiza rețelelor sociale, aceste tipuri de noduri ar putea fi înțelese ca tipuri de oameni care îi ajută pe alții să găsească noi locuri de muncă sau să facă noi conexiuni - sunt ușile către cercuri sociale necunoscute anterior.

Pe care ar trebui să folosesc?

Măsura adecvată a centralității depinde de scopul analizei dvs. Doriți să știți ce utilizatori sunt evidențiați frecvent de alții în ceea ce privește cantitatea? Centralitatea gradului ar fi probabil cea mai bună opțiune. Sau preferați o măsură de centralitate care să ia în considerare calitatea? În acest caz, vectorul propriu sau PageRank vor da rezultate mai bune. Dacă doriți să știți care utilizatori funcționează cel mai eficient ca punți între diferite comunități, interacțiunea este cea mai bună opțiune.

Când utilizați mai multe măsuri similare, de exemplu, vector propriu și PageRank, puteți să le estimați pe toate și să vedeți dacă dau clasamente echivalente. Dacă nu, puteți aprofunda analiza diferențelor sau puteți genera o nouă măsură combinând scorurile acestora.

O altă abordare utilizează analiza componentelor principale pentru a estima care măsură vă oferă mai multe informații despre influența reală a nodurilor în rețeaua dumneavoastră.

Calcul practic al centralității

Să vedem cum putem calcula aceste măsuri folosind R și RStudio. (Se pot face și cu Gephi.)

Mai întâi, trebuie să încărcăm toate bibliotecile pe care le vom folosi în acest articol:

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

În continuare, vom elimina nodurile izolate din datele pe care le-am folosit anterior, deoarece acestea nu sunt utile pentru această analiză. Apoi, vom folosi funcțiile igraph betweenness , centr_eigen , page_rank și degree pentru a estima măsurile de centralitate. În cele din urmă, vom stoca scorurile pe obiectul igraph și pe un cadru de date pentru a vedea care utilizatori au fost cei mai 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)

Acum putem verifica cei 10 cei mai centrali utilizatori după fiecare măsură:

grad
 top_n(cent, 10 ,degr)%>% arrange(desc(degr))%>% select(degr)
Vector propriu
 top_n(cent, 10 ,eig)%>% arrange(desc(eig))%>% select(eig)
PageRank
 top_n(cent, 10 ,prank)%>% arrange(desc(prank))%>% select(prank)
Între mijloc
 top_n(cent, 10 ,bet)%>% arrange(desc(bet))%>% select(bet)

Rezultatele:

grad Vector propriu PageRank Între mijloc
ESPNFC 5892 PSG_înăuntru 1 mundodabola 0,037 vederidey 77704
TrollFotbal 5755 CrewsMat19 0,51 AleLiparoti 0,026 EdmundOris 76425
PSG_înăuntru 5194 eh01195991 0,4 PSG_înăuntru 0,017 ba*****lla 63799
CrewsMat19 4344 mohammad135680 0,37 RoyNemer 0,016 FranciscoGaius 63081
brfotbal 4054 ActuFoot_ 0,34 TrollFotbal 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 brfotbal 0,25 instantFoot 0,007 Zyyon_ 57269
FootyHumour 2976 SaylorMoonArmy 0,22 IbaiOut 0,006 CrewsMat19 53758
mundodabola 2778 JohnsvillPat 0,2 2010 MisterChip 0,006 MdeenOlawale 49572

Putem vedea că primele trei măsuri au în comun un număr de utilizatori, cum ar fi PSG_inside, ESPNFC, CrewsMat19 și TrollFootball. Putem presupune că au avut o influență puternică asupra discuției. Betweenness are o abordare diferită pentru măsurarea centralității și, prin urmare, nu arată atât de multă suprapunere cu celelalte tehnici.

Notă: opiniile exprimate de conturile de Twitter menționate în acest articol nu reflectă cele ale Toptal sau ale autorului.

În imaginile următoare, puteți vedea graficul nostru original de rețea colorat cu două suprapuneri de etichete de utilizator. În primul, nodurile sunt evidențiate prin scorurile lor PageRank, iar în al doilea, prin scorurile lor de întreținere:

O imagine care arată o diagramă PageRank colorată, cu primii 10 utilizatori și rețelele lor evidențiate. Cei mai mari trei utilizatori sunt PSG_inside, TrollFootball și ESPNFC. ESPNFC este situat în stânga parcelei și este colorat în violet, în timp ce PSG_inside este plasat în dreapta acestuia, colorat în roșu. TrollFootball este situat mai sus și în dreapta acestora, între utilizatorii de culoare verde, albastru și portocaliu.
Discuția lui Messi cu primii 10 utilizatori PageRank evidențiați

O imagine care arată o diagramă colorată, cu primii 10 utilizatori și rețelele lor etichetate și evidențiate. Toți primii 10 utilizatori, care au dimensiuni mai asemănătoare decât în ​​imaginea anterioară, sunt localizați în colțul din stânga jos al imaginii, care este colorat în violet. Ele sunt grupate strâns.
Discuția lui Messi cu primii 10 utilizatori intermedii evidențiate

Gephi poate fi folosit pentru a reproduce aceste imagini. Puteți estima scorurile intermediare sau PageRank folosind butonul Network Diameter din panoul de statistici. Apoi, puteți afișa numele nodurilor folosind atribute așa cum este demonstrat în prima tranșă din această serie.

Analiza textului: R și LDA

De asemenea, putem analiza discuțiile din rețelele sociale pentru a identifica despre ce au vorbit utilizatorii. Există mai multe moduri de a aborda acest lucru. Vom face modelarea subiectelor prin Latent Dirichlet Allocation (LDA), o tehnică de învățare automată nesupravegheată care ne permite să estimăm ce set de cuvinte tind să apară împreună. Apoi, prin acel set de cuvinte, putem deduce subiectul discutat.

Primul pas este igienizarea textului. Pentru a face acest lucru, definim următoarea funcție:

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

De asemenea, trebuie să eliminăm cuvintele oprite, duplicatele și intrările goale. În continuare, trebuie să ne convertim textul într-o matrice document-termen care să fie procesată de LDA.

În acest set de date, avem utilizatori care vorbesc în multe limbi (engleză, spaniolă, franceză etc.). LDA funcționează cel mai bine dacă ne concentrăm pe o singură limbă. O vom aplica asupra utilizatorilor celei mai mari comunități detectate în prima tranșă a acestei serii, care este compusă în principal din conturi cu utilizatori vorbitori de limba engleză.

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

Numărul subiectelor și scorurile de coerență

Principalul hiperparametru pe care trebuie să-l definim în LDA este numărul (k) de subiecte pe care dorim să le estimăm. Totuși, cum putem ști asta dinainte? O abordare comună este antrenarea modelelor LDA pe diferite valori k și măsurarea coerenței fiecăruia. Vom face acest lucru pentru k valori de la 3 la 20, deoarece valorile din afara acestui interval nu merită verificate, din experiența mea:

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

În continuare, graficăm valoarea de coerență a fiecăruia:

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

O valoare mare de coerență arată o mai bună segmentare a textului pe subiecte:

Un grafic care arată scorul de coerență pentru diferite subiecte. Scorul de coerență variază de la puțin peste 0,05 pe șase până la șapte subiecte, cu trei până la 12 subiecte având toate un scor sub 0,065. Scorul ajunge brusc la aproximativ 0,105 pentru 13 subiecte. Apoi scade sub 0,06 pentru 17 subiecte, până la aproape 0,09 pentru 19 subiecte și se termină cu puțin peste 0,07 pentru 20 de subiecte.

Atingem scorul de coerență maxim cu k = 13, așa că vom folosi modelul LDA antrenat cu 13 subiecte. Prin funcția GetTopTerms, putem vedea cele 10 cuvinte principale pentru fiecare subiect și putem estima semantica subiectului prin ele:

 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

Următorul tabel detaliază cele mai importante cinci subiecte detectate și cele 10 cuvinte principale care le exemplifica:

t_1 t_2 t_3 t_4 t_5
1 messi messi messi messi messi
2 lionel instagram ligă EST psg
3 lionel_messi post victorie il Leu
4 psg milion obiective au leo_messi
5 Madrid îi place cap se toarnă acum
6 real spo ionii pas compa
7 Barcelona capră ch_ioni cu va
8 Paris psg ucl du ser
9 real Madrid bar balon qui jucător
10 mbapp mai mare lume je mai bine

Deși majoritatea utilizatorilor din această comunitate sunt vorbitori de engleză, există încă un număr de vorbitori de franceză și spaniolă (t_4 și t_5 în tabel). Putem deduce că primul subiect se referă la echipa anterioară a lui Messi (FC Barcelona), al doilea subiect este despre postarea lui Messi pe Instagram, iar al treilea subiect se concentrează pe realizările lui Messi.

Acum că avem subiectele, putem prezice care dintre ele a fost cel mai discutat. Pentru a face asta, mai întâi vom concatena tweet-urile utilizatorilor (din nou, din cea mai mare comunitate):

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

Apoi, dezinfectăm textul ca înainte și creăm DTM-ul. După aceea, numim funcția de predict folosind modelul nostru LDA și DTM-ul ca argumente. De asemenea, am setat metoda la Gibbs pentru a îmbunătăți timpul de calcul, deoarece avem mult text de analizat:

 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)

Acum, în cadrul de date com1.users.topics , vedem cât de mult a vorbit fiecare utilizator despre fiecare subiect:

Cont t_1 t_2 t_3 t_4 t_5 […]
___99 0,02716049 0,86666666 0,00246913 0,00246913 0,00246913
șeful__ 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
[…]

În cele din urmă, cu aceste informații, putem crea un nou atribut pe graficul nodului pentru a defini despre ce subiect s-a vorbit cel mai mult de către care utilizator. Apoi putem crea un nou fișier GML pentru a-l vizualiza în 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 grafic nod colorat generat folosind Gephi, arătând ESPNFC drept utilizatorul cu cel mai înalt rang în funcție de centralitatea PageRank. ESPNFC este situat în partea de jos a imaginii, cu multe noduri violet grupate dedesubt.
Cea mai mare comunitate de discuții despre Messi colorată în funcție de subiect și cu utilizatorii evidențiați de centralitatea PageRank

O imagine care arată procentul de utilizatori evidențiați de fiecare culoare utilizată în grafic, cu violetul „t 6” fiind culoarea cea mai folosită (40,53% din toți utilizatorii din grafic), urmată de verde „t 13” la 11.02 % și albastru/cian „t 10” la 9,68%. Un „NA” gri, în penultima poziție a acestei liste de 11, reprezintă 2,25%.
Etichetele subiectelor și procentul de utilizatori pentru fiecare culoare utilizată în grafic

Deducerea subiectelor importante și aplicarea centralității rețelei sociale

În prima parte a acestei serii, am învățat cum să obținem date de pe Twitter, să creăm graficul de interacțiune, să-l trasăm prin Gephi și să detectăm comunități și utilizatori importanți. În această tranșă, am extins această analiză demonstrând utilizarea unor criterii suplimentare pentru a detecta utilizatorii influenți. De asemenea, am demonstrat cum să detectăm și să deducem despre ce vorbeau utilizatorii și cum să trasăm asta în rețea.

În următorul nostru articol, vom continua să aprofundăm această analiză, arătând cum utilizatorii pot detecta flagelul rețelelor sociale: spamboții și trolii.

Citiți suplimentare pe blogul Toptal Engineering:

  • Analiza rețelelor sociale folosind Power BI și R: un ghid vizual personalizat