Понимание динамики Twitter с помощью R и Gephi: анализ текста и центральность
Опубликовано: 2022-07-22Эта статья расширяет и углубляет анализ, представленный в первой части нашей серии анализов социальных сетей. Мы используем тот же набор данных Twitter и сеть взаимодействия, которые были созданы в первой статье. Однако на этот раз идея состоит в том, чтобы лучше определить основных действующих лиц, определить их темы для обсуждения и понять, как эти темы распространяются.
Центральность социальных сетей
Для достижения наших целей сначала нам нужно ввести понятие центральности . В науке о сетях центральность относится к узлам, которые имеют сильное влияние на сеть. Влияние — неоднозначное понятие; это можно понять по-разному. Является ли узел с большим количеством ребер более влиятельным, чем узел с меньшим количеством ребер, но более «важными»? Что является важным преимуществом в социальной сети?
Чтобы устранить эти неясности, сетевые ученые разработали множество показателей центральности. Здесь мы обсудим четыре часто используемых меры, хотя доступно гораздо больше.
Степень
Наиболее распространенной и интуитивно понятной мерой является степень центральности. Идея центральности по степени проста: измеряйте влияние по степени узла. Может иметь варианты, если граф направленный; в этом случае вы можете измерить входящую и исходящую степень — первая известна как оценка центра, а вторая — как оценка авторитета.
В первой части этой серии мы использовали ненаправленный подход. На этот раз мы сосредоточимся на подходе в степени. Это позволяет проводить более точный анализ, выделяя пользователей, которых другие ретвитят, по сравнению с пользователями, которые просто часто ретвитят.
собственный вектор
Мера собственного вектора основана на степени центральности. Чем больше влиятельных узлов указывает на данный узел, тем выше его оценка. Мы начинаем с матрицы смежности , где строки и столбцы представляют узлы, и мы используем 1 или 0, чтобы указать, связаны ли соответствующие узлы данной строки и столбца. Основной расчет оценивает собственные векторы матрицы. Главный собственный вектор будет содержать нужные нам меры центральности, где позиция i будет содержать показатель центральности узла i .
PageRank
PageRank — это вариант меры собственного вектора, лежащий в основе Google. Точный метод, который использует Google, неизвестен, но общая идея заключается в том, что каждый узел начинает с оценки 1, а затем распределяет свою оценку равными частями по каждому из своих ребер. Например, если от узла отходят три ребра, он «отправляет» одну треть своей оценки через каждое ребро. В то же время узел становится более важным из-за ребер, указывающих на него. Это приводит к разрешимой системе N уравнений с N неизвестными.
Посредничество
Четвертая мера, промежуточность , использует совсем другой подход. Здесь узел считается влиятельным, если он включен во множество коротких путей между другими узлами. То есть он отвечает за связь со многими другими узлами, соединяя «разные миры».
Например, в анализе социальных сетей такие типы узлов можно понимать как типы людей, которые помогают другим найти новую работу или завязать новые связи — они являются дверьми в ранее неизвестные социальные круги.
Что я должен использовать?
Соответствующая мера центральности зависит от цели вашего анализа. Вы хотите знать, какие пользователи часто выделяются другими с точки зрения количества? Центральность по степени, вероятно, будет вашим лучшим вариантом. Или вы предпочитаете меру центральности, учитывающую качество? В этом случае собственный вектор или PageRank дадут лучшие результаты. Если вы хотите знать, какие пользователи наиболее эффективно функционируют в качестве мостов между различными сообществами, промежуточность — ваш лучший вариант.
При использовании нескольких одинаковых показателей, например, собственного вектора и PageRank, вы можете оценить их все и посмотреть, дают ли они эквивалентные рейтинги. Если нет, вы можете углубить свой анализ различий или создать новую меру, объединив их оценки.
Другой подход использует анализ основных компонентов, чтобы оценить, какая мера дает вам больше информации о реальном влиянии узлов на вашу сеть.
Практический расчет центральности
Давайте посмотрим, как мы можем рассчитать эти показатели с помощью R и RStudio. (Их также можно сделать с помощью Gephi.)
Во-первых, нам нужно загрузить все библиотеки, которые мы будем использовать в этой статье:
library("plyr") library(igraph) library(tidyverse) library(NLP) library("tm") library(RColorBrewer) library(wordcloud) library(topicmodels) library(SnowballC) library("textmineR")
Далее мы удалим изолированные узлы из данных, которые мы использовали ранее, так как они бесполезны для этого анализа. Затем мы будем использовать функции igraph
betweenness
, centr_eigen
, page_rank
и degree
для оценки показателей центральности. Наконец, мы будем хранить оценки в объекте igraph
и во фрейме данных, чтобы увидеть, какие пользователи были наиболее важными.
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)
Теперь мы можем проверить 10 самых центральных пользователей по каждому показателю:
Степень | |
собственный вектор | |
PageRank | |
Посредничество | |
Результаты, достижения:
Степень | собственный вектор | PageRank | Посредничество | ||||
---|---|---|---|---|---|---|---|
ЕСПНФК | 5892 | ПСЖ_внутри | 1 | мундодабола | 0,037 | взглядыдей | 77704 |
Футбольный троль | 5755 | CrewsMat19 | 0,51 | АлеЛипароти | 0,026 | ЭдмундОрис | 76425 |
ПСЖ_внутри | 5194 | eh01195991 | 0,4 | ПСЖ_внутри | 0,017 | бас***лла | 63799 |
CrewsMat19 | 4344 | Мохаммад135680 | 0,37 | РойНемер | 0,016 | ФрансискоГайус | 63081 |
брфутбол | 4054 | ActuFoot_ | 0,34 | Футбольный троль | 0,013 | Йемихазан | 62534 |
PSG_espanol | 3616 | Мартвалл | 0,34 | ЕСПНФК | 0,01 | хэштег2weet | 61123 |
ИбайАут | 3258 | ЕСПНФК | 0,3 | PSG_espanol | 0,007 | Анжела_FCB | 60991 |
ActuFoot_ | 3175 | брфутбол | 0,25 | InstantFoot | 0,007 | Zyyon_ | 57269 |
МутныйЮмор | 2976 | СейлорЛунаАрмия | 0,22 | ИбайАут | 0,006 | CrewsMat19 | 53758 |
мундодабола | 2778 | ДжонсвиллПэт | 0,2 | 2010МистерЧип | 0,006 | МдинОлавале | 49572 |
Мы видим, что первые три показателя имеют общее количество пользователей, таких как PSG_inside, ESPNFC, CrewsMat19 и TrollFootball. Можно предположить, что они оказали сильное влияние на дискуссию. Промежуточность имеет другой подход к измерению центральности и поэтому не показывает такого большого совпадения с другими методами.
Примечание. Мнения, выраженные учетными записями Twitter, упомянутыми в этой статье, не отражают точку зрения Toptal или автора.
На следующих изображениях вы можете увидеть наш исходный цветной сетевой график с двумя наложенными пользовательскими метками. В первом случае узлы выделены их показателями PageRank, а во втором — их показателями промежуточности:
Gephi можно использовать для воспроизведения этих изображений. Вы можете оценить промежуточность или баллы PageRank, используя кнопку Network Diameter на панели статистики. Затем вы можете отображать имена узлов, используя атрибуты, как показано в первой части этой серии.
Анализ текста: R и LDA
Мы также можем анализировать обсуждения в социальных сетях, чтобы определить, о чем говорили пользователи. Есть несколько подходов к этому. Мы будем проводить тематическое моделирование с помощью скрытого распределения Дирихле (LDA), метода машинного обучения без учителя, который позволяет нам оценить, какой набор слов обычно появляется вместе. Затем с помощью этого набора слов мы можем вывести обсуждаемую тему.
Первый шаг — очистка текста. Для этого определим следующую функцию:
# 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) }
Нам также необходимо удалить стоп-слова, дубликаты и пустые записи. Затем мы должны преобразовать наш текст в матрицу терминов документа для обработки LDA.
В этом наборе данных у нас есть пользователи, говорящие на многих языках (английский, испанский, французский и т. д.). LDA работает лучше всего, если мы сосредоточимся на одном языке. Мы собираемся применить его к пользователям самого большого сообщества, обнаруженного в первой части этой серии, которое состоит в основном из учетных записей с англоязычными пользователями.
# 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))
Подсчет тем и оценка согласованности
Основной гиперпараметр, который нам нужно определить в LDA, — это количество (k) тем, которые мы хотим оценить. Однако как мы можем знать это заранее? Один из распространенных подходов заключается в обучении моделей LDA для разных значений k и измерении когерентности каждого из них. Мы сделаем это для значений k от 3 до 20, поскольку, по моему опыту, значения за пределами этого диапазона проверять не стоит:
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"))
Далее мы наносим на график значение когерентности каждого:
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")
Высокое значение когерентности показывает лучшую сегментацию текста на темы:
Мы достигаем пика согласованности при k = 13, поэтому будем использовать модель LDA, обученную по 13 темам. С помощью функции GetTopTerms мы можем увидеть 10 основных слов для каждой темы и через них оценить семантику темы:
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
В следующей таблице подробно описаны пять наиболее важных обнаруженных тем и 10 основных слов, которые их иллюстрируют:
t_1 | т_2 | т_3 | т_4 | т_5 | |
---|---|---|---|---|---|
1 | Месси | Месси | Месси | Месси | Месси |
2 | Лайонел | инстаграм | лига | стандартное восточное время | псж |
3 | Лионель Месси | почта | победить | иль | Лео |
4 | псж | миллион | цели | ау | leo_messi |
5 | Мадрид | нравится | ч | налить | ахора |
6 | настоящий | спо | ионы | па | компа |
7 | барселона | козел | ch_ions | авек | ва |
8 | Париж | псж | УКЛ | дю | сер |
9 | Реал Мадрид | бар | баллон | куй | югадор |
10 | mbapp | больше | Мир | Дже | старший |
Хотя большинство пользователей в этом сообществе говорят по-английски, все еще есть несколько носителей французского и испанского языков (t_4 и t_5 в таблице). Мы можем сделать вывод, что первая тема связана с предыдущей командой Месси (ФК «Барселона»), вторая тема касается поста Месси в Instagram, а третья тема посвящена достижениям Месси.
Теперь, когда у нас есть темы, мы можем предсказать, какая из них была наиболее обсуждаемой. Для этого мы сначала объединим твиты пользователей (опять же, из самого большого сообщества):
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 = " "))
Затем мы очищаем текст, как и раньше, и создаем DTM. После этого мы вызываем функцию predict
, используя нашу модель LDA и DTM в качестве аргументов. Кроме того, мы установили метод Гиббса, чтобы сократить время вычислений, потому что нам нужно проанализировать много текста:
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)
Теперь во фрейме данных com1.users.topics
мы видим, сколько каждый пользователь говорил по каждой теме:
Счет | t_1 | т_2 | т_3 | т_4 | т_5 | […] |
---|---|---|---|---|---|---|
___99-й | 0,02716049 | 0,86666666 | 0,00246913 | 0,00246913 | 0,00246913 | |
Босс__ | 0,05185185 | 0,84197530 | 0,00246913 | 0,00246913 | 0,00246913 | |
Мемфис | 0,00327868 | 0,00327868 | 0,03606557 | 0,00327868 | 0,00327868 | |
___Алекс1 | 0,00952380 | 0,00952380 | 0,00952380 | 0,00952380 | 0,00952380 | |
[…] |
Наконец, с помощью этой информации мы можем создать новый атрибут на диаграмме узлов, чтобы определить, какая тема больше всего обсуждалась каким пользователем. Затем мы можем создать новый файл GML, чтобы визуализировать его в 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")
Определение важных тем и применение центральности в социальных сетях
В первой части этой серии мы узнали, как получать данные из Twitter, создавать граф взаимодействия, отображать его с помощью Gephi и обнаруживать сообщества и важных пользователей. В этой части мы расширили этот анализ, продемонстрировав использование дополнительных критериев для выявления влиятельных пользователей. Мы также продемонстрировали, как обнаруживать и делать выводы о том, о чем говорили пользователи, и отображать это в сети.
В нашей следующей статье мы продолжим углублять этот анализ, показав, как пользователи могут обнаружить бич социальных сетей: спам-ботов и троллей.
Дальнейшее чтение в блоге Toptal Engineering:
- Анализ социальных сетей с помощью Power BI и R: Руководство по пользовательским визуальным элементам