Twitter-Dynamik mit R und Gephi verstehen: Textanalyse und Zentralität

Veröffentlicht: 2022-07-22

Dieser Artikel erweitert und vertieft die Analyse, die im ersten Teil unserer Serie zur Analyse sozialer Netzwerke vorgestellt wurde. Wir verwenden denselben Twitter-Datensatz und dasselbe Interaktionsnetzwerk, das im ersten Artikel erstellt wurde. Diesmal geht es jedoch darum, die Hauptakteure besser abzuleiten, ihre Diskussionsthemen zu identifizieren und zu verstehen, wie sich diese Themen ausbreiten.

Zentralität sozialer Netzwerke

Um unsere Ziele zu erreichen, müssen wir zunächst das Konzept der Zentralität einführen. In der Netzwerkwissenschaft bezieht sich Zentralität auf Knoten, die einen starken Einfluss auf das Netzwerk haben. Einfluss ist ein zweideutiges Konzept; es kann auf viele Arten verstanden werden. Ist ein Knoten mit vielen Kanten einflussreicher als ein Knoten mit weniger, aber „wichtigeren“ Kanten? Was macht einen wichtigen Vorteil in einem sozialen Netzwerk aus?

Um diese Mehrdeutigkeiten anzugehen, haben Netzwerkwissenschaftler viele Zentralitätsmaße entwickelt. Hier diskutieren wir vier häufig verwendete Maßnahmen, obwohl viele weitere verfügbar sind.

Grad

Das gebräuchlichste und intuitivste Maß ist die Gradzentralität. Die Idee hinter der Gradzentralität ist einfach: Messen Sie den Einfluss durch den Grad des Knotens. Es kann Varianten haben, wenn der Graph gerichtet ist; In diesem Fall können Sie den Grad und den Grad messen – der erste ist als Hub-Score und der zweite als Autoritäts-Score bekannt.

Im ersten Teil dieser Serie haben wir den ungerichteten Ansatz verwendet. Dieses Mal konzentrieren wir uns auf den In-Grad-Ansatz. Dies ermöglicht eine genauere Analyse, indem Benutzer, die von anderen retweetet werden, gegenüber Benutzern hervorgehoben werden, die lediglich häufig retweeten.

Eigenvektor

Das Eigenvektormaß baut auf Gradzentralität auf. Je mehr einflussreiche Knoten auf einen bestimmten Knoten zeigen, desto höher ist seine Punktzahl. Wir beginnen mit einer Adjazenzmatrix , in der Zeilen und Spalten Knoten darstellen, und wir verwenden eine 1 oder 0, um anzugeben, ob die entsprechenden Knoten einer bestimmten Zeile und Spalte verbunden sind. Die Hauptberechnung schätzt die Eigenvektoren der Matrix. Der Haupteigenvektor enthält die gewünschten Zentralitätsmaße, wobei die Position i den Zentralitätswert des Knotens i enthält.

Seitenrang

PageRank ist die Variation des Eigenvektormaßes im Kern von Google. Die genaue Methode, die Google verwendet, ist unbekannt, aber die allgemeine Idee ist, dass jeder Knoten mit einer Punktzahl von 1 beginnt und seine Punktzahl dann zu gleichen Teilen auf jede seiner Kanten verteilt. Wenn zum Beispiel ein Knoten drei Kanten hat, die von ihm ausgehen, „sendet“ er ein Drittel seiner Punktzahl durch jede Kante. Gleichzeitig wird der Knoten durch die Kanten, die auf ihn zeigen, wichtiger. Dies führt zu einem lösbaren System von N Gleichungen mit N Unbekannten.

Dazwischen

Die vierte Maßnahme, betweenness , verwendet einen ganz anderen Ansatz. Hier wird ein Knoten als einflussreich bezeichnet, wenn er in viele kurze Pfade zwischen anderen Knoten eingebunden ist. Das heißt, es ist für die Kommunikation mit vielen anderen Knoten verantwortlich und verbindet „verschiedene Welten“.

In der Analyse sozialer Netzwerke könnten solche Knoten beispielsweise als die Art von Menschen verstanden werden, die anderen helfen, neue Jobs zu finden oder neue Kontakte zu knüpfen – sie sind die Türen zu zuvor unbekannten sozialen Kreisen.

Welche sollte ich verwenden?

Das geeignete Zentralitätsmaß hängt vom Ziel Ihrer Analyse ab. Sie wollen wissen, welche Nutzer häufig mengenmäßig von anderen herausgegriffen werden? Gradzentralität wäre wahrscheinlich die beste Option. Oder bevorzugen Sie ein Zentralitätsmaß, das Qualität berücksichtigt? In diesem Fall liefern Eigenvektor oder PageRank bessere Ergebnisse. Wenn Sie wissen möchten, welche Benutzer am effektivsten als Brücken zwischen verschiedenen Gemeinschaften fungieren, ist betweenness Ihre beste Option.

Wenn Sie mehrere ähnliche Maße verwenden, z. B. Eigenvektor und PageRank, können Sie alle schätzen und sehen, ob sie zu gleichwertigen Rankings führen. Wenn nicht, können Sie Ihre Analyse der Unterschiede vertiefen oder eine neue Kennzahl generieren, indem Sie ihre Werte kombinieren.

Ein anderer Ansatz verwendet die Hauptkomponentenanalyse, um abzuschätzen, welche Messung Ihnen mehr Informationen über den tatsächlichen Einfluss der Knoten auf Ihr Netzwerk gibt.

Praktische Berechnung der Zentralität

Sehen wir uns an, wie wir diese Maße mit R und RStudio berechnen können. (Sie können auch mit Gephi durchgeführt werden.)

Zuerst müssen wir alle Bibliotheken laden, die wir in diesem Artikel verwenden werden:

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

Als nächstes entfernen wir isolierte Knoten aus den zuvor verwendeten Daten, da sie für diese Analyse nicht nützlich sind. Dann werden wir die igraph -Funktionen centr_eigen betweenness page_rank und degree verwenden, um die Zentralitätsmaße zu schätzen. Schließlich speichern wir die Ergebnisse im igraph -Objekt und in einem Datenrahmen, um zu sehen, welche Benutzer am zentralsten waren.

 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)

Jetzt können wir die 10 zentralsten Benutzer nach jeder Maßnahme überprüfen:

Grad
 top_n(cent, 10 ,degr)%>% arrange(desc(degr))%>% select(degr)
Eigenvektor
 top_n(cent, 10 ,eig)%>% arrange(desc(eig))%>% select(eig)
Seitenrang
 top_n(cent, 10 ,prank)%>% arrange(desc(prank))%>% select(prank)
Dazwischen
 top_n(cent, 10 ,bet)%>% arrange(desc(bet))%>% select(bet)

Die Ergebnisse:

Grad Eigenvektor Seitenrang Dazwischen
ESPNFC 5892 PSG_innen 1 mundodabola 0,037 Ansichten 77704
TrollFußball 5755 CrewsMat19 0,51 AleLiparoti 0,026 Edmund Oris 76425
PSG_innen 5194 eh01195991 0,4 PSG_innen 0,017 ba*****lla 63799
CrewsMat19 4344 mohammad135680 0,37 RoyNemer 0,016 FranciscoGaius 63081
brfußball 4054 ActuFoot_ 0,34 TrollFußball 0,013 Yemihazan 62534
PSG_espanol 3616 martvall 0,34 ESPNFC 0,01 hashtag2weet 61123
IbaiOut 3258 ESPNFC 0,3 PSG_espanol 0,007 Angela_FCB 60991
ActuFoot_ 3175 brfußball 0,25 InstantFoot 0,007 Zyyon_ 57269
FootyHumor 2976 SaylorMoonArmy 0,22 IbaiOut 0,006 CrewsMat19 53758
mundodabola 2778 JohnsvillPat 0,2 2010MisterChip 0,006 MdeenOlawale 49572

Wir können sehen, dass die ersten drei Maßnahmen eine Reihe von Benutzern teilen, z. B. PSG_inside, ESPNFC, CrewsMat19 und TrollFootball. Wir können davon ausgehen, dass sie die Diskussion stark beeinflusst haben. Betweenness hat einen anderen Ansatz zur Messung der Zentralität und zeigt daher nicht so viele Überschneidungen mit den anderen Techniken.

Hinweis: Die Ansichten, die von den in diesem Artikel erwähnten Twitter-Konten geäußert werden, spiegeln nicht die von Toptal oder dem Autor wider.

In den folgenden Bildern sehen Sie unser ursprüngliches farbiges Netzwerkdiagramm mit zwei überlagerten Benutzeretiketten. Im ersten Fall werden die Knoten durch ihre PageRank-Werte hervorgehoben, im zweiten durch ihre Betweenness-Werte:

Ein Bild, das ein farbiges PageRank-Diagramm zeigt, wobei die Top 10 Benutzer und ihre Netzwerke hervorgehoben sind. Die drei größten Nutzer sind PSG_inside, TrollFootball und ESPNFC. ESPNFC befindet sich auf der linken Seite des Diagramms und ist lila gefärbt, während PSG_inside rechts davon platziert ist und rot gefärbt ist. TrollFootball befindet sich höher und rechts von ihnen, zwischen grünen, blauen und orangefarbenen Benutzern.
Messi-Diskussion mit den Top 10 PageRank-Benutzern hervorgehoben

Ein Bild, das ein farbiges Betweenness-Diagramm zeigt, wobei die Top-10-Benutzer und ihre Netzwerke beschriftet und hervorgehoben sind. Alle Top-10-Benutzer, die ähnlich groß sind wie im vorherigen Bild, befinden sich in der unteren linken Ecke des Bildes, die violett gefärbt ist. Sie sind eng zusammen gruppiert.
Messi-Diskussion mit den Top 10 Betweenness-Benutzern hervorgehoben

Gephi kann verwendet werden, um diese Bilder zu reproduzieren. Sie können den Betweenness- oder PageRank-Score mithilfe der Schaltfläche Netzwerkdurchmesser im Statistikbereich schätzen. Anschließend können Sie mithilfe von Attributen Knotennamen anzeigen, wie im ersten Teil dieser Reihe gezeigt.

Textanalyse: R und LDA

Wir können auch Diskussionen in sozialen Netzwerken analysieren, um festzustellen, worüber Benutzer gesprochen haben. Es gibt mehrere Möglichkeiten, dies anzugehen. Wir werden Themenmodellierung durch Latent Dirichlet Allocation (LDA) durchführen, eine unbeaufsichtigte maschinelle Lerntechnik, mit der wir abschätzen können, welche Sätze von Wörtern zusammen erscheinen. Dann können wir durch diese Wortgruppe auf das diskutierte Thema schließen.

Der erste Schritt besteht darin, den Text zu bereinigen. Dazu definieren wir folgende Funktion:

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

Wir müssen auch Stoppwörter, Duplikate und leere Einträge entfernen. Als nächstes müssen wir unseren Text in eine Dokument-Term-Matrix umwandeln, die von LDA verarbeitet werden soll.

In diesem Datensatz sprechen Benutzer in vielen Sprachen (Englisch, Spanisch, Französisch usw.). LDA funktioniert am besten, wenn wir uns auf eine einzige Sprache konzentrieren. Wir werden es auf die Benutzer der größten Community anwenden, die in der ersten Folge dieser Serie entdeckt wurde, die hauptsächlich aus Konten mit englischsprachigen Benutzern besteht.

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

Themenzählungen und Kohärenzwerte

Der wichtigste Hyperparameter, den wir in LDA definieren müssen, ist die Anzahl (k) der Themen, die wir schätzen möchten. Aber wie können wir es vorher wissen? Ein üblicher Ansatz besteht darin, LDA-Modelle über verschiedene k -Werte zu trainieren und die Kohärenz jedes einzelnen zu messen. Wir tun dies für k -Werte von 3 bis 20, da Werte außerhalb dieses Bereichs meiner Erfahrung nach keine Überprüfung wert sind:

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

Als nächstes stellen wir den Kohärenzwert von jedem grafisch dar:

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

Ein hoher Kohärenzwert zeigt eine bessere Segmentierung des Textes in Themen:

Ein Diagramm, das den Kohärenzwert für verschiedene Themen zeigt. Der Kohärenzwert variiert von etwas über 0,05 bei sechs bis sieben Themen, wobei drei bis 12 Themen alle einen Wert unter 0,065 haben. Die Punktzahl erreicht plötzlich einen Spitzenwert von etwa 0,105 für 13 Themen. Dann geht es für 17 Themen unter 0,06, für 19 Themen auf fast 0,09 und endet bei knapp über 0,07 für 20 Themen.

Wir erreichen unseren höchsten Kohärenzwert mit k = 13, also verwenden wir das mit 13 Themen trainierte LDA-Modell. Durch die GetTopTerms-Funktion können wir die 10 Hauptwörter für jedes Thema sehen und die Themensemantik durch sie einschätzen:

 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

Die folgende Tabelle enthält die fünf wichtigsten erkannten Themen und die 10 wichtigsten Wörter, die sie beispielhaft darstellen:

t_1 t_2 t_3 t_4 t_5
1 Messi Messi Messi Messi Messi
2 Löwe instagram Liga Europäische Sommerzeit Psg
3 lionel_messi Post gewinnen il Löwe
4 Psg Million Tore Au leo_messi
5 Madrid Likes CH gießen Ahor
6 real spo Ionen pas Kompa
7 Barcelona Ziege ch_ions mit va
8 Paris Psg ukl du ser
9 Real Madrid Bar Ballon qui jugador
10 mbapp größer Welt je mejor

Obwohl die meisten Benutzer in dieser Community Englisch sprechen, gibt es immer noch eine Reihe von Französisch- und Spanischsprachigen (t_4 und t_5 in der Tabelle). Wir können daraus schließen, dass sich das erste Thema auf Messis vorheriges Team (FC Barcelona) bezieht, das zweite Thema sich auf Messis Post auf Instagram bezieht und das dritte Thema sich auf Messis Erfolge konzentriert.

Jetzt, da wir die Themen haben, können wir vorhersagen, welches davon am meisten diskutiert wurde. Dazu werden wir zunächst Tweets von Benutzern (wiederum aus der größten Community) verketten:

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

Dann bereinigen wir den Text wie zuvor und erstellen den DTM. Danach rufen wir die predict mit unserem LDA-Modell und dem DTM als Argumente auf. Außerdem setzen wir die Methode auf Gibbs, um die Rechenzeit zu verbessern, da wir viel Text zu analysieren haben:

 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)

Jetzt sehen wir im com1.users.topics , wie viel jeder Benutzer über jedes Thema gesprochen hat:

Konto t_1 t_2 t_3 t_4 t_5 […]
___99 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
[…]

Schließlich können wir mit diesen Informationen ein neues Attribut im Knotendiagramm erstellen, um zu definieren, über welches Thema von welchem ​​​​Benutzer am meisten gesprochen wurde. Dann können wir eine neue GML-Datei erstellen, um sie in Gephi zu visualisieren:

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

Ein farbiges Knotendiagramm, das mit Gephi erstellt wurde und ESPNFC als Benutzer mit dem höchsten Rang nach PageRank-Zentralität zeigt. ESPNFC befindet sich am unteren Rand des Bildes, darunter sind viele violette Knoten gruppiert.
Größte Community von Messi-Diskussionen, gefärbt nach Themen und mit Benutzern, die durch PageRank-Zentralität hervorgehoben werden

Ein Bild, das den Prozentsatz der Benutzer zeigt, die durch jede im Diagramm verwendete Farbe hervorgehoben werden, wobei das violette „t 6“ die am häufigsten verwendete Farbe ist (40,53 % aller Benutzer im Diagramm), gefolgt vom grünen „t 13“ um 11:02 Uhr % und Blau/Cyan "t 10" bei 9,68 %. Ein graues „NA“ an vorletzter Stelle dieser Liste von 11 macht 2,25 % aus.
Themenbezeichnungen und Prozentsatz der Benutzer für jede im Diagramm verwendete Farbe

Wichtige Themen ableiten und die Zentralität sozialer Netzwerke anwenden

Im ersten Teil dieser Serie haben wir gelernt, wie man Daten von Twitter erhält, das Interaktionsdiagramm erstellt, es durch Gephi zeichnet und Communitys und wichtige Benutzer erkennt. In dieser Folge haben wir diese Analyse erweitert, indem wir die Verwendung zusätzlicher Kriterien zur Erkennung einflussreicher Benutzer demonstriert haben. Wir haben auch demonstriert, wie man erkennen und ableiten kann, worüber die Benutzer sprachen, und dies im Netzwerk darstellen.

In unserem nächsten Artikel werden wir diese Analyse weiter vertiefen, indem wir zeigen, wie Benutzer die Geißel der sozialen Medien erkennen können: Spambots und Trolle.

Weiterführende Literatur im Toptal Engineering Blog:

  • Social Network Analysis Using Power BI and R: A Custom Visuals Guide