Memahami Dinamika Twitter Dengan R dan Gephi: Analisis dan Sentralitas Teks
Diterbitkan: 2022-07-22Artikel ini memperluas dan memperdalam analisis yang disajikan dalam angsuran pertama dari seri analisis jaringan sosial kami. Kami menggunakan dataset Twitter dan jaringan interaksi yang sama yang dibangun di artikel pertama. Namun, kali ini, idenya adalah untuk menyimpulkan aktor utama dengan lebih baik, mengidentifikasi topik diskusi mereka, dan memahami bagaimana topik ini menyebar.
Sentralitas Jaringan Sosial
Untuk mencapai tujuan kita, pertama-tama kita perlu memperkenalkan konsep sentralitas . Dalam ilmu jaringan, sentralitas mengacu pada node yang memiliki pengaruh kuat pada jaringan. Pengaruh adalah konsep yang ambigu; itu bisa dipahami dengan banyak cara. Apakah node dengan banyak edge lebih berpengaruh daripada node dengan edge yang lebih sedikit tetapi lebih "penting"? Apa yang merupakan keunggulan penting di jejaring sosial?
Untuk mengatasi ambiguitas ini, para ilmuwan jaringan telah mengembangkan banyak ukuran sentralitas. Di sini, kita membahas empat ukuran yang umum digunakan, meskipun banyak lagi yang tersedia.
Derajat
Ukuran yang paling umum dan intuitif adalah sentralitas derajat. Gagasan di balik sentralitas derajat sederhana: Ukur pengaruh dengan derajat simpul. Ini dapat memiliki varian jika grafik diarahkan; dalam hal ini, Anda dapat mengukur derajat masuk dan derajat keluar—yang pertama dikenal sebagai skor hub dan yang kedua sebagai skor otoritas.
Dalam angsuran pertama dari seri ini, kami menggunakan pendekatan tidak terarah. Kali ini, kami fokus pada pendekatan indegree. Ini memungkinkan analisis yang lebih akurat dengan menekankan pengguna yang di-retweet oleh orang lain daripada pengguna yang hanya sering me-retweet.
vektor eigen
Ukuran vektor eigen dibangun di atas sentralitas derajat. Semakin banyak simpul yang berpengaruh menunjuk ke simpul tertentu, semakin tinggi skornya. Kita mulai dengan matriks adjacency , di mana baris dan kolom mewakili node, dan kita menggunakan 1 atau 0 untuk menunjukkan apakah node yang sesuai dari baris dan kolom tertentu terhubung. Perhitungan utama memperkirakan vektor eigen matriks. Vektor eigen utama akan berisi ukuran sentralitas yang kita inginkan, di mana posisi i akan menampung skor sentralitas dari simpul i .
Peringkat halaman
PageRank adalah variasi ukuran vektor eigen di inti Google. Metode pasti yang digunakan Google tidak diketahui, tetapi ide umumnya adalah bahwa setiap node dimulai dengan skor 1, kemudian mendistribusikan skornya di bagian yang sama ke setiap tepinya. Misalnya, jika sebuah simpul memiliki tiga tepi yang memanjang darinya, ia "mengirim" sepertiga dari skornya melalui setiap tepi. Pada saat yang sama, simpul dibuat lebih penting oleh tepi yang mengarah ke sana. Ini menghasilkan sistem persamaan N yang dapat dipecahkan dengan N tidak diketahui.
Antara
Ukuran keempat, betweenness , menggunakan pendekatan yang sangat berbeda. Di sini, sebuah node dikatakan berpengaruh jika termasuk dalam banyak jalur pendek antara node lainnya. Artinya, ia bertanggung jawab untuk berkomunikasi dengan banyak node lain, menghubungkan "dunia yang berbeda."
Misalnya, dalam analisis jejaring sosial, simpul semacam ini dapat dipahami sebagai tipe orang yang membantu orang lain menemukan pekerjaan baru atau membuat koneksi baru—mereka adalah pintu ke lingkaran sosial yang sebelumnya tidak dikenal.
Mana yang Harus Saya Gunakan?
Ukuran sentralitas yang tepat tergantung pada tujuan analisis Anda. Apakah Anda ingin tahu pengguna mana yang sering dipilih oleh orang lain dalam hal kuantitas? Sentralitas gelar kemungkinan akan menjadi pilihan terbaik Anda. Atau apakah Anda lebih suka ukuran sentralitas yang mempertimbangkan kualitas? Dalam hal ini, eigenvector atau PageRank akan memberikan hasil yang lebih baik. Jika Anda ingin mengetahui pengguna mana yang berfungsi paling efektif sebagai jembatan antara komunitas yang berbeda, keberantaraan adalah pilihan terbaik Anda.
Saat menggunakan beberapa ukuran serupa, misalnya, vektor eigen dan PageRank, Anda dapat memperkirakan semuanya dan melihat apakah mereka menghasilkan peringkat yang setara. Jika tidak, Anda dapat memperdalam analisis Anda tentang perbedaan atau menghasilkan ukuran baru dengan menggabungkan skor mereka.
Pendekatan lain menggunakan analisis komponen utama untuk memperkirakan ukuran mana yang memberi Anda lebih banyak informasi tentang pengaruh nyata dari node pada jaringan Anda.
Perhitungan Sentralitas Langsung
Mari kita lihat bagaimana kita dapat menghitung ukuran ini menggunakan R dan RStudio. (Mereka juga dapat dilakukan dengan Gephi.)
Pertama, kita perlu memuat semua perpustakaan yang akan kita gunakan di seluruh artikel ini:
library("plyr") library(igraph) library(tidyverse) library(NLP) library("tm") library(RColorBrewer) library(wordcloud) library(topicmodels) library(SnowballC) library("textmineR")
Selanjutnya, kami akan menghapus node terisolasi dari data yang kami gunakan sebelumnya, karena tidak berguna untuk analisis ini. Kemudian, kita akan menggunakan fungsi igraph
betweenness
, centr_eigen
, page_rank
, dan degree
untuk memperkirakan ukuran sentralitas. Terakhir, kami akan menyimpan skor pada objek igraph
dan pada bingkai data untuk melihat pengguna mana yang paling sentral.
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)
Sekarang kita dapat memeriksa 10 pengguna paling sentral dengan setiap ukuran:
Derajat | |
vektor eigen | |
Peringkat halaman | |
Antara | |
Hasil:
Derajat | vektor eigen | Peringkat halaman | Antara | ||||
---|---|---|---|---|---|---|---|
ESPNFC | 5892 | PSG_inside | 1 | mundodabola | 0,037 | pemandangan | 77704 |
TrollSepakbola | 5755 | CrewsMat19 | 0,51 | AleLiparoti | 0,026 | EdmundOris | 76425 |
PSG_inside | 5194 | eh01195991 | 0.4 | PSG_inside | 0,017 | ba******l | 63799 |
CrewsMat19 | 4344 | mohammad135680 | 0.37 | Roy Nemer | 0,016 | Francisco Gaius | 63081 |
sepak bola | 4054 | ActuFoot_ | 0.34 | TrollSepakbola | 0,013 | Yemihazan | 62534 |
PSG_espanol | 3616 | marttvall | 0.34 | ESPNFC | 0,01 | tagar2weet | 61123 |
IbaiOut | 3258 | ESPNFC | 0,3 | PSG_espanol | 0,007 | Angela_FCB | 60991 |
ActuFoot_ | 3175 | sepak bola | 0,25 | Kaki Instan | 0,007 | Zyyon_ | 57269 |
FootyHumor | 2976 | SaylorBulanTentara | 0,22 | IbaiOut | 0,006 | CrewsMat19 | 53758 |
mundodabola | 2778 | JohnsvillPat | 0.2 | 2010MisterChip | 0,006 | MdeenOlawale | 49572 |
Kita dapat melihat bahwa tiga langkah pertama berbagi sejumlah pengguna, seperti PSG_inside, ESPNFC, CrewsMat19, dan TrollFootball. Kita dapat berasumsi bahwa mereka memiliki pengaruh yang kuat atas diskusi. Antara memiliki pendekatan yang berbeda untuk mengukur sentralitas dan karena itu tidak menunjukkan banyak tumpang tindih dengan teknik lainnya.
Catatan: Pandangan yang diungkapkan oleh akun Twitter yang disebutkan dalam artikel ini tidak mencerminkan pandangan Toptal atau penulisnya.
Pada gambar berikut, Anda dapat melihat grafik jaringan berwarna asli kami dengan dua lapisan label pengguna. Yang pertama, node disorot oleh skor PageRank mereka, dan yang kedua, dengan skor antara mereka:
Gephi dapat digunakan untuk mereproduksi gambar-gambar ini. Anda dapat memperkirakan skor antara atau PageRank menggunakan tombol Diameter Jaringan di panel statistik. Kemudian, Anda dapat menampilkan nama node menggunakan atribut seperti yang ditunjukkan pada angsuran pertama dalam seri ini.
Analisis Teks: R dan LDA
Kami juga dapat menganalisis diskusi jejaring sosial untuk mengidentifikasi apa yang dibicarakan pengguna. Ada beberapa cara untuk mendekati ini. Kami akan melakukan pemodelan topik melalui Latent Dirichlet Allocation (LDA), sebuah teknik pembelajaran mesin tanpa pengawasan yang memungkinkan kami memperkirakan kumpulan kata mana yang cenderung muncul bersamaan. Kemudian, melalui rangkaian kata tersebut, kita dapat menyimpulkan topik yang sedang dibahas.
Langkah pertama adalah membersihkan teks. Untuk melakukan itu, kita mendefinisikan fungsi berikut:
# 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) }
Kita juga perlu menghapus stopword, duplikat, dan entri kosong. Selanjutnya, kita harus mengonversi teks kita menjadi matriks istilah dokumen untuk diproses oleh LDA.
Dalam kumpulan data ini, kami memiliki pengguna yang berbicara dalam banyak bahasa (Inggris, Spanyol, Prancis, dll.). LDA bekerja paling baik jika kita fokus pada satu bahasa. Kami akan menerapkannya pada pengguna komunitas terbesar yang terdeteksi dalam angsuran pertama seri ini, yang sebagian besar terdiri dari akun dengan pengguna berbahasa Inggris.
# 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))
Jumlah Topik dan Skor Koherensi
Hyperparameter utama yang perlu kita definisikan di LDA adalah jumlah (k) topik yang ingin kita perkirakan. Namun, bagaimana kita bisa mengetahuinya sebelumnya? Salah satu pendekatan umum adalah melatih model LDA pada nilai k yang berbeda dan mengukur koherensi masing-masing. Kami akan melakukan ini untuk nilai k dari 3 hingga 20, karena nilai di luar rentang ini tidak layak untuk diperiksa, menurut pengalaman saya:
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"))
Selanjutnya, kami membuat grafik nilai koherensi masing-masing:
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")
Nilai koherensi yang tinggi menunjukkan segmentasi teks yang lebih baik ke dalam topik:
Kami mencapai skor koherensi puncak kami dengan k = 13, jadi kami akan menggunakan model LDA yang dilatih dengan 13 topik. Melalui fungsi GetTopTerms, kita dapat melihat 10 kata utama untuk setiap topik dan memperkirakan semantik topik melalui mereka:
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
Tabel berikut merinci lima topik terpenting yang terdeteksi dan 10 kata utama yang menunjukkannya:
t_1 | t_2 | t_3 | t_4 | t_5 | |
---|---|---|---|---|---|
1 | messi | messi | messi | messi | messi |
2 | singa | liga | Est | psg | |
3 | Lionel Messi | pos | menang | aku | Leo |
4 | psg | juta | sasaran | au | leo_messi |
5 | Madrid | suka | ch | menuangkan | ahora |
6 | nyata | spo | ion | pas | teman |
7 | barcelona | kambing | ch_ion | avec | va |
8 | Paris | psg | ucl | du | ser |
9 | Real Madrid | batang | balon | qui | jugador |
10 | mbap | lebih besar | dunia | ya | besar |
Meskipun sebagian besar pengguna di komunitas ini adalah penutur bahasa Inggris, masih ada sejumlah penutur bahasa Prancis dan Spanyol (t_4 dan t_5 dalam tabel). Dapat kita simpulkan bahwa topik pertama berkaitan dengan tim Messi sebelumnya (FC Barcelona), topik kedua tentang postingan Messi di Instagram, dan topik ketiga berfokus pada pencapaian Messi.
Sekarang setelah kita memiliki topik, kita dapat memprediksi topik mana yang paling banyak dibahas. Untuk melakukan itu, pertama-tama kami akan menggabungkan tweet oleh pengguna (sekali lagi, dari komunitas terbesar):
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 = " "))
Kemudian, kami membersihkan teks seperti sebelumnya dan membuat DTM. Setelah itu, kami memanggil fungsi predict
menggunakan model LDA kami dan DTM sebagai argumen. Selain itu, kami menetapkan metode ke Gibbs untuk meningkatkan waktu komputasi karena kami memiliki banyak teks untuk dianalisis:
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)
Sekarang, dalam bingkai data com1.users.topics
, kita melihat seberapa banyak setiap pengguna berbicara tentang setiap topik:
Akun | t_1 | t_2 | t_3 | t_4 | t_5 | […] |
---|---|---|---|---|---|---|
___99 | 0,02716049 | 0.86666666 | 0,00246913 | 0,00246913 | 0,00246913 | |
Bos__ | 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 | |
[…] |
Akhirnya, dengan informasi ini, kita dapat membuat atribut baru pada grafik simpul untuk menentukan topik mana yang paling banyak dibicarakan oleh pengguna mana. Kemudian kita dapat membuat file GML baru untuk memvisualisasikannya di 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")
Menyimpulkan Topik Penting dan Menerapkan Sentralitas Jaringan Sosial
Dalam angsuran pertama dari seri ini, kami belajar cara mendapatkan data dari Twitter, membuat grafik interaksi, memplotnya melalui Gephi, dan mendeteksi komunitas dan pengguna penting. Dalam angsuran ini, kami memperluas analisis ini dengan menunjukkan penggunaan kriteria tambahan untuk mendeteksi pengguna yang berpengaruh. Kami juga mendemonstrasikan cara mendeteksi dan menyimpulkan apa yang dibicarakan pengguna dan merencanakannya di jaringan.
Dalam artikel kami berikutnya, kami akan terus memperdalam analisis ini dengan menunjukkan bagaimana pengguna dapat mendeteksi momok media sosial: robot spam dan troll.
Bacaan Lebih Lanjut di Blog Teknik Toptal:
- Analisis Jaringan Sosial Menggunakan Power BI dan R: Panduan Visual Kustom