فهم ديناميكيات Twitter باستخدام R و Gephi: تحليل النص والمركزية

نشرت: 2022-07-22

توسع هذه المقالة وتعمق التحليل المقدم في الدفعة الأولى من سلسلة تحليل الشبكات الاجتماعية الخاصة بنا. نستخدم نفس مجموعة بيانات Twitter وشبكة التفاعل التي تم إنشاؤها في المقالة الأولى. ومع ذلك ، فإن الفكرة هذه المرة هي استنتاج الجهات الفاعلة الرئيسية بشكل أفضل ، وتحديد موضوعات المناقشة الخاصة بهم ، وفهم كيفية انتشار هذه الموضوعات.

مركزية الشبكة الاجتماعية

لتحقيق أهدافنا ، نحتاج أولاً إلى تقديم مفهوم المركزية . في علم الشبكات ، تشير المركزية إلى العقد التي لها تأثير قوي على الشبكة. التأثير مفهوم غامض. يمكن فهمه بعدة طرق. هل العقدة ذات الحواف المتعددة أكثر تأثيرًا من العقدة ذات الحواف الأقل ولكن الأكثر "أهمية"؟ ما الذي يشكل ميزة مهمة على الشبكة الاجتماعية؟

لمعالجة هذه الالتباسات ، طور علماء الشبكات العديد من مقاييس المركزية. هنا ، نناقش أربعة مقاييس شائعة الاستخدام ، على الرغم من توفر المزيد منها.

درجة

المقياس الأكثر شيوعًا وبديهية هو مركزية الدرجة. الفكرة الكامنة وراء مركزية الدرجة بسيطة: قم بقياس التأثير بواسطة درجة العقدة. يمكن أن يكون لها متغيرات إذا تم توجيه الرسم البياني ؛ في هذه الحالة ، يمكنك قياس الدرجة المستقلة والدرجة الخارجية — يُعرف الأول باسم درجة المحور والثاني باسم درجة السلطة.

في الجزء الأول من هذه السلسلة ، استخدمنا الطريقة غير الموجهة. هذه المرة ، نركز على النهج المستقل. يتيح ذلك تحليلًا أكثر دقة من خلال التأكيد على المستخدمين الذين أعادوا تغريدهم من قبل الآخرين على المستخدمين الذين يعيدون تغريدهم بشكل متكرر.

Eigenvector

يعتمد مقياس eigenvector على مركزية الدرجة. وكلما زادت العقد المؤثرة التي تشير إلى عقدة معينة ، زادت نقاطها. نبدأ بمصفوفة متجاورة ، حيث تمثل الصفوف والأعمدة العقد ، ونستخدم 1 أو 0 للإشارة إلى ما إذا كانت العقد المقابلة لصف وعمود معينين متصلة أم لا. يقدّر الحساب الرئيسي المتجهات الذاتية للمصفوفة. سيحتوي ناقل eigenvector الرئيسي على مقاييس المركزية التي نريدها ، حيث سيحتفظ الموضع i بدرجة مركزية العقدة i .

رتبة صفحة

PageRank هو تباين مقياس eigenvector في صميم Google. الطريقة الدقيقة التي يستخدمها Google غير معروفة ، ولكن الفكرة العامة هي أن كل عقدة تبدأ بعلامة 1 ، ثم توزع نتيجتها في أجزاء متساوية على كل من حوافها. على سبيل المثال ، إذا كانت العقدة لها ثلاثة حواف تمتد منها ، فإنها "ترسل" ثلث نقاطها عبر كل حافة. في الوقت نفسه ، تصبح العقدة أكثر أهمية من خلال الحواف التي تشير إليها. ينتج عن هذا نظام قابل للحل من معادلات N مع N مجهولة.

بين

المقياس الرابع ، البينية ، يستخدم نهجًا مختلفًا تمامًا. هنا ، يُقال أن العقدة مؤثرة إذا تم تضمينها في العديد من المسارات القصيرة بين العقد الأخرى. أي أنها مسؤولة عن التواصل مع العديد من العقد الأخرى ، وربط "عوالم مختلفة".

على سبيل المثال ، في تحليل الشبكات الاجتماعية ، يمكن فهم هذه الأنواع من العقد على أنها أنواع الأشخاص الذين يساعدون الآخرين في العثور على وظائف جديدة أو إنشاء روابط جديدة - فهم الأبواب لدوائر اجتماعية لم تكن معروفة من قبل.

ما الذي يجب علي استخدامه؟

يعتمد مقياس المركزية المناسب على هدف تحليلك. هل تريد معرفة المستخدمين الذين يتم تحديدهم بشكل متكرر من قبل الآخرين من حيث الكمية؟ من المحتمل أن تكون مركزية الدرجة هي خيارك الأفضل. أم تفضل مقياس مركزية يأخذ بعين الاعتبار الجودة؟ في هذه الحالة ، سيعطي eigenvector أو PageRank نتائج أفضل. إذا كنت تريد معرفة المستخدمين الذين يعملون بشكل أكثر فاعلية كجسور بين المجتمعات المختلفة ، فإن البينية هي أفضل خيار لك.

عند استخدام مقاييس متعددة مماثلة ، على سبيل المثال ، eigenvector و 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 مستخدمين مركزيين حسب كل مقياس:

درجة
 top_n(cent, 10 ,degr)%>% arrange(desc(degr))%>% select(degr)
Eigenvector
 top_n(cent, 10 ,eig)%>% arrange(desc(eig))%>% select(eig)
رتبة صفحة
 top_n(cent, 10 ,prank)%>% arrange(desc(prank))%>% select(prank)
بين
 top_n(cent, 10 ,bet)%>% arrange(desc(bet))%>% select(bet)

النتائج:

درجة Eigenvector رتبة صفحة بين
ESPNFC 5892 داخل PSG 1 مونودابولا 0.037 الآراء 77704
ترول كرة القدم 5755 الطاقم 0.51 AleLiparoti 0.026 ادموند اوريس 76425
داخل PSG 5194 eh01195991 0.4 داخل PSG 0.017 با ***** لا 63799
الطاقم 4344 محمد 135680 0.37 روي نمر 0.016 فرانسيسكو جايوس 63081
brfootball 4054 أكتوفوت 0.34 ترول كرة القدم 0.013 يميحزان 62534
PSG_espanol 3616 مارتفال 0.34 ESPNFC 0.01 hashtag2weet 61123
IbaiOut 3258 ESPNFC 0.3 PSG_espanol 0.007 Angela_FCB 60991
أكتوفوت 3175 brfootball 0.25 lnstantFoot 0.007 زيون 57269
فوتيهومور 2976 SaylorMoonArmy 0.22 IbaiOut 0.006 الطاقم 53758
مونودابولا 2778 JohnsvillPat 0.2 2010MisterChip 0.006 مدين أولوالي 49572

يمكننا أن نرى أن المقاييس الثلاثة الأولى تشترك في عدد من المستخدمين ، مثل PSG_inside و ESPNFC و CrewsMat19 و TrollFootball. يمكننا أن نفترض أن لديهم تأثيرًا قويًا على المناقشة. تتميز البينية بمقاربة مختلفة لقياس المركزية ، وبالتالي لا تظهر قدرًا كبيرًا من التداخل مع الأساليب الأخرى.

ملاحظة: الآراء التي عبرت عنها حسابات تويتر المذكورة في هذا المقال لا تعكس آراء Toptal أو المؤلف.

في الصور التالية ، يمكنك مشاهدة الرسم البياني الأصلي الملون للشبكة مع تراكبتين لتسمية المستخدم. في الأول ، يتم تمييز العقد من خلال درجات PageRank الخاصة بها ، وفي الثانية ، من خلال نتائجها بين الدرجات:

صورة تعرض مخطط PageRank ملون ، مع تمييز أفضل 10 مستخدمين وشبكاتهم. أكبر ثلاثة مستخدمين هم PSG_inside و TrollFootball و ESPNFC. يقع ESPNFC على يسار قطعة الأرض ويلون بنفسجي ، بينما يتم وضع PSG_inside على يمينه باللون الأحمر. تقع لعبة TrollFootball أعلى وعلى يمينها ، بين المستخدمين ذوي اللون الأخضر والأزرق والبرتقالي.
تم تسليط الضوء على مناقشة ميسي مع أفضل 10 مستخدمين في نظام ترتيب الصفحات

صورة تعرض مخططًا ملونًا بين التباعد ، مع تصنيف وتمييز أفضل 10 مستخدمين وشبكاتهم. جميع المستخدمين العشرة الأوائل ، الذين هم أكثر تشابهًا في الحجم من الصورة السابقة ، موجودون في الزاوية اليسرى السفلية من الصورة ، وهي ملونة باللون الأرجواني. يتم تجميعهم معًا بإحكام.
تم تسليط الضوء على مناقشة ميسي مع أفضل 10 مستخدمين بين الجنسين

يمكن استخدام Gephi لإعادة إنتاج هذه الصور. يمكنك تقدير الدرجات البينية أو PageRank باستخدام زر قطر الشبكة في لوحة الإحصائيات. بعد ذلك ، يمكنك إظهار أسماء العقد باستخدام السمات كما هو موضح في الدفعة الأولى من هذه السلسلة.

تحليل النص: R و LDA

يمكننا أيضًا تحليل مناقشات الشبكات الاجتماعية لتحديد ما يتحدث عنه المستخدمون. هناك طرق متعددة للتعامل مع هذا. سنقوم بعمل نمذجة للموضوعات من خلال تخصيص Latent Dirichlet Allocation (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")

تُظهر قيمة التماسك العالية تقسيمًا أفضل للنص إلى مواضيع:

رسم بياني يوضح درجة الاتساق لمواضيع مختلفة. تختلف درجة الاتساق من ما يزيد قليلاً عن 0.05 في ستة إلى سبعة موضوعات ، مع وجود ثلاثة إلى 12 موضوعًا جميعها بها درجة أقل من 0.065. تصل النتيجة إلى ذروتها فجأة عند 0.105 في 13 موضوعًا. ثم ينخفض ​​إلى أقل من 0.06 لـ 17 موضوعًا ، ويصل إلى ما يقرب من 0.09 لـ 19 موضوعًا ، وينتهي عند أعلى بقليل من 0.07 في 20 موضوعًا.

لقد وصلنا إلى درجة ذروة الاتساق مع k = 13 ، لذلك سنستخدم نموذج LDA المدرب على 13 موضوعًا. من خلال وظيفة GetTopTerms ، يمكننا رؤية الكلمات الرئيسية العشر لكل موضوع وتقدير دلالات الموضوع من خلالها:

 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

يوضح الجدول التالي أهم خمسة موضوعات تم الكشف عنها والكلمات الرئيسية العشر التي تمثلها:

t_1 t_2 t_3 t_4 t_5
1 ميسي ميسي ميسي ميسي ميسي
2 ليونيل الانستغرام الدوري EST psg
3 ليونيل ميسي بريد فوز انا ليو
4 psg مليون الأهداف au ليو ميسي
5 مدريد الإعجابات الفصل يصب أهورا
6 حقا spo الأيونات باس كومبا
7 برشلونة ماعز ch_ions افيك va
8 باريس psg ucl دو سر
9 ريال مدريد شريط بالون كوي جوجادور
10 mbapp أكبر العالمية جي ميجور

على الرغم من أن معظم المستخدمين في هذا المجتمع هم من المتحدثين باللغة الإنجليزية ، لا يزال هناك عدد من المتحدثين بالفرنسية والإسبانية (t_4 و t_5 في الجدول). يمكننا أن نستنتج أن الموضوع الأول يتعلق بفريق ميسي السابق (برشلونة) ، والموضوع الثاني حول منشور ميسي على إنستغرام ، والموضوع الثالث يركز على إنجازات ميسي.

الآن بعد أن أصبح لدينا الموضوعات ، يمكننا التنبؤ بأي منها كان الأكثر مناقشة. للقيام بذلك ، سنقوم أولاً بتجميع تغريدات المستخدمين (مرة أخرى ، من أكبر مجتمع):

 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 كوسيطات. أيضًا ، قمنا بتعيين الطريقة على Gibbs لتحسين وقت الحوسبة لأن لدينا الكثير من النصوص لتحليلها:

 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 t_2 t_3 t_4 t_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") 

رسم بياني للعقدة الملونة تم إنشاؤه باستخدام Gephi ، ويظهر ESPNFC باعتباره المستخدم الأعلى تصنيفًا من خلال مركزية PageRank. يقع ESPNFC بالقرب من الجزء السفلي من الصورة ، مع وجود العديد من العقد الأرجواني المتجمعة تحتها.
أكبر مجتمع لنقاش ميسي ملون حسب الموضوع ومع المستخدمين المميزين من خلال مركزية PageRank

صورة توضح النسبة المئوية للمستخدمين المميزين بكل لون مستخدم في الرسم البياني ، حيث يمثل اللون الأرجواني "t 6" اللون الأكثر استخدامًا (40.53٪ من جميع المستخدمين في الرسم البياني) ، يليه اللون الأخضر "t 13" عند 11.02 ٪ والأزرق / السماوي "t 10" بنسبة 9.68٪. يمثل "NA" الرمادي ، في المركز الثاني إلى الأخير من هذه القائمة الـ 11 ، 2.25٪.
تسميات الموضوع والنسبة المئوية للمستخدمين لكل لون مستخدم في الرسم البياني

استنتاج الموضوعات المهمة وتطبيق مركزية الشبكة الاجتماعية

في الجزء الأول من هذه السلسلة ، تعلمنا كيفية الحصول على البيانات من Twitter ، وإنشاء الرسم البياني للتفاعل ، ورسمه من خلال Gephi ، واكتشاف المجتمعات والمستخدمين المهمين. في هذه الحلقة ، قمنا بتوسيع هذا التحليل من خلال توضيح استخدام معايير إضافية للكشف عن المستخدمين المؤثرين. لقد أوضحنا أيضًا كيفية اكتشاف واستنتاج ما كان يتحدث عنه المستخدمون ورسم ذلك في الشبكة.

في مقالتنا التالية ، سنواصل تعميق هذا التحليل من خلال إظهار كيف يمكن للمستخدمين اكتشاف بلاء وسائل التواصل الاجتماعي: spambots و trolls.

مزيد من القراءة على مدونة Toptal Engineering:

  • تحليل الشبكة الاجتماعية باستخدام Power BI و R: دليل مرئي مخصص