Интернет-исследование одних из самых скандальных выборов XXI века
О чем это исследование? В 2016-м году прошли выборы президента США, победу в которых одержал текущий президент страны Дональд Трамп. В этом тексте мы проанализируем твиты Трампа (@realDonaldTrump ) и его оппонентки из Демократической партии Хиллари Клинтон (@HillaryClinton ).
Импортируем файл
Code
Attaching package: 'dplyr'
The following objects are masked from 'package:stats':
filter, lag
The following objects are masked from 'package:base':
intersect, setdiff, setequal, union
Code
library (tidyr)
library (ggplot2)
library (tidytext)
library (textdata)
library (wordcloud)
Loading required package: RColorBrewer
Code
library (tidytext)
library (tidyverse)
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ forcats 1.0.0 ✔ readr 2.1.5
✔ lubridate 1.9.4 ✔ stringr 1.6.0
✔ purrr 1.1.0 ✔ tibble 3.3.0
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag() masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
Code
tweets <- read.csv ("Yandex.Disk.localized/tweets 2.csv" , stringsAsFactors = FALSE )
# 2. СОЗДАЕМ КАТЕГОРИЮ КАНДИДАТА
tweets$ candidate <- ifelse (tweets$ handle == "HillaryClinton" , "Clinton" , "Trump" )
Считаем сколько у нас твитов всего: в том числе и у каждого кандидата отдельно.
Code
cat ("Клинтон:" , sum (tweets$ candidate == "Clinton" ))
Code
cat (" \n Трамп:" , sum (tweets$ candidate == "Trump" ))
Code
cat (" \n Всего:" , nrow (tweets))
Code
# Высчитываем хэштеги
hashtags <- str_extract_all (tweets$ text, "#[A-Za-z0-9_]+" ) %>% unlist ()
hashtag_counts <- table (tolower (hashtags)) %>% sort (decreasing = TRUE )
print (head (hashtag_counts, 5 ))
#trump2016 #makeamericagreatagain #votetrump
348 254 68
#americafirst #rncincle
67 67
Что это значит? C хэштегами получается интересная ситуация: 4 из 5 наиболее распространенных напрямую связаны с Трампом: будь то напрямую его фамилия или его же лозунги типа “americafirst” или “makeamericagreatagain”. И это несмотря на то, что общее количество твитов с минимальной разницей больше у Клинтон. С высоты 2025-го года уже давно известно, кто в той гонке одержал победу – но в этом исследовании через открытые данные я выделю основные аспекты ведения соцсетей республиканским кандидатом, которые помогли ему получить поддержку электората.
А пока что токенизируем наши данные.
Code
tokens_df <- tweets %>%
unnest_tokens (word, text) %>%
anti_join (stop_words) %>%
filter (! str_detect (word, "https|t \\ .co|[0-9]" ))
Joining with `by = join_by(word)`
5. ЧАСТОТНЫЙ АНАЛИЗ
word_freq <- tokens_df %>% group_by(candidate, word) %>% summarise(n = n(), .groups = “drop”) %>% arrange(desc(n))
Теперь визуализируем самые распространенные слова из твитов обоиз кандидатов. Ввиду особого оформления текстов на твиттере, включая различные элементы (https, @, t// и тд), фильтрация будет специфической. # 6. ВИЗУАЛИЗАЦИЯ 1: Топ слова
Code
word_freq <- tweets %>%
unnest_tokens (word, text) %>%
anti_join (stop_words) %>%
filter (! str_detect (word, "^https|^t \\ .co|^@|^[0-9]+$" )) %>%
count (candidate, word, sort = TRUE )
Joining with `by = join_by(word)`
Code
top_words <- word_freq |>
group_by (candidate) |>
top_n (10 , n) |>
ungroup ()
ggplot (top_words, aes (x = reorder (word, n), y = n, fill = candidate)) +
geom_col () +
coord_flip () +
facet_wrap (~ candidate, scales = "free" ) +
labs (title = "Топ-10 слов по кандидатам" , x = "Слово" , y = "Частота" ) +
scale_fill_manual (values = c ("Clinton" = "blue" , "Trump" = "red" )) +
theme_minimal ()
Code
# 7. ТАБЛИЦА: Сравнение
summary_table <- tweets |>
group_by (candidate) |>
summarise (
tweets = n (),
avg_retweets = mean (as.numeric (retweet_count), na.rm = TRUE ),
avg_favorites = mean (as.numeric (favorite_count), na.rm = TRUE )
)
Теперь анализиируем эмоциональную составляющую текста:
Code
positive_words <- c ("good" , "great" , "love" , "win" , "hope" , "strong" , "best" )
negative_words <- c ("bad" , "wrong" , "fail" , "lie" , "fake" , "stupid" )
sentiment_analysis <- tokens_df %>%
mutate (sentiment = case_when (
word %in% positive_words ~ 1 ,
word %in% negative_words ~ - 1 ,
TRUE ~ 0
)) %>%
group_by (candidate) %>%
summarise (
positive = sum (sentiment == 1 ),
negative = sum (sentiment == - 1 ),
net_sentiment = positive - negative
)
print (sentiment_analysis)
# A tibble: 2 × 4
candidate positive negative net_sentiment
<chr> <int> <int> <int>
1 Clinton 142 71 71
2 Trump 194 143 51
10. TF-IDF (характерные слова)
tf_idf <- word_freq %>% bind_tf_idf(word, candidate, n) %>% arrange(desc(tf_idf))
cat(“=== ХАРАКТЕРНЫЕ СЛОВА (TF-IDF) ===”) print(tf_idf %>% group_by(candidate) %>% top_n(3, tf_idf))
cat(“=== ВЫВОДЫ ===”) cat(“1. Трамп активнее в Twitter”) cat(“2. Клинтон чаще использует хэштеги о выборах”) cat(“3. У Трампа выше вовлеченность (ретвиты)”) cat(“4. Лексика кандидатов существенно различается”)
АНАЛИЗ ТВИТОВ КЛИНТОН И ТРАМПА
Автор: [Ваше Имя], 2024-04-10
library(tidyverse) library(tidytext)
1. ЗАГРУЗКА И ПРЕПРОЦЕССИНГ
tweets <- read.csv(“tweets 2.csv”, stringsAsFactors = FALSE)
Проверяем структуру данных
cat(“=== ИНФОРМАЦИЯ О ДАТАСЕТЕ ===”) cat(“Колонки:”, names(tweets), “”) cat(“Всего строк:”, nrow(tweets), “”)
2. СОЗДАЕМ ПЕРЕМЕННУЮ CANDIDATE
tweets <- tweets %>% mutate(candidate = case_when( handle == “HillaryClinton” ~ “Clinton”, handle == “realDonaldTrump” ~ “Trump”, TRUE ~ “Other” ))
Проверяем распределение
cat(“=== РАСПРЕДЕЛЕНИЕ ПО КАНДИДАТАМ ===”) print(table(tweets$candidate))
3. ОСНОВНАЯ СТАТИСТИКА
cat(“=== ОСНОВНАЯ СТАТИСТИКА ===”) cat(“Твитов Клинтон:”, sum(tweets\(candidate == "Clinton"), "\n")
cat("Твитов Трампа:", sum(tweets\) candidate == “Trump”), “”) cat(“Общее количество:”, nrow(tweets), “”)
4. РЕГУЛЯРНЫЕ ВЫРАЖЕНИЯ - хэштеги
cat(“=== АНАЛИЗ ХЭШТЕГОВ ===”)
Извлекаем хэштеги с помощью регулярных выражений
hashtags_list <- str_extract_all(tweets$text, “#[A-Za-z0-9_]+”)
Преобразуем в вектор и подсчитываем
all_hashtags <- unlist(hashtags_list) if(length(all_hashtags) > 0) { hashtag_counts <- table(tolower(all_hashtags)) top_hashtags <- sort(hashtag_counts, decreasing = TRUE)[1:10] print(top_hashtags) } else { cat(“Хэштеги не найдены”) }
5. ТОКЕНИЗАЦИЯ И ЧАСТОТНЫЙ АНАЛИЗ
cat(“=== ТОКЕНИЗАЦИЯ ===”)
Токенизация с фильтрацией
tokens_df <- tweets %>% select(text, candidate, retweet_count, favorite_count) %>% unnest_tokens(word, text) %>% anti_join(stop_words, by = “word”) %>% filter(!str_detect(word, “https| t\.co|@| [0-9]+$”))
6. ТАБЛИЦА: Частотный анализ
cat(“=== ТАБЛИЦА: Топ слова по кандидатам ===”)
word_freq_table <- tokens_df %>% count(candidate, word, sort = TRUE) %>% group_by(candidate) %>% slice_max(n, n = 10) %>% ungroup()
print(word_freq_table)
7. ВИЗУАЛИЗАЦИЯ 1: Топ слова
p1 <- word_freq_table %>% mutate(word = reorder_within(word, n, candidate)) %>% ggplot(aes(x = n, y = word, fill = candidate)) + geom_col(show.legend = FALSE) + facet_wrap(~candidate, scales = “free_y”) + scale_y_reordered() + labs(title = “Топ-10 слов по кандидатам”, x = “Частота”, y = “Слово”) + theme_minimal() + scale_fill_manual(values = c(“Clinton” = “#0072B2”, “Trump” = “#D55E00”))
print(p1)
8. ТАБЛИЦА 2: Статистика популярности
cat(“=== ТАБЛИЦА: Статистика популярности ===”)
popularity_table <- tweets %>% group_by(candidate) %>% summarise( tweets_count = n(), avg_retweets = mean(as.numeric(retweet_count), na.rm = TRUE), avg_favorites = mean(as.numeric(favorite_count), na.rm = TRUE), total_retweets = sum(as.numeric(retweet_count), na.rm = TRUE), total_favorites = sum(as.numeric(favorite_count), na.rm = TRUE) ) %>% mutate(engagement_rate = (total_retweets + total_favorites) / tweets_count)
print(popularity_table)
9. ВИЗУАЛИЗАЦИЯ 2: Популярность
p2 <- popularity_table %>% select(candidate, avg_retweets, avg_favorites) %>% pivot_longer(cols = c(avg_retweets, avg_favorites), names_to = “metric”, values_to = “value”) %>% mutate(metric = ifelse(metric == “avg_retweets”, “Средние ретвиты”, “Средние лайки”)) %>% ggplot(aes(x = candidate, y = value, fill = candidate)) + geom_col(position = “dodge”) + facet_wrap(~metric, scales = “free”) + labs(title = “Вовлеченность аудитории”, x = “Кандидат”, y = “Количество”) + theme_minimal() + scale_fill_manual(values = c(“Clinton” = “#0072B2”, “Trump” = “#D55E00”))
print(p2)
10. АНАЛИЗ ДЛИНЫ ТВИТОВ
tweets <- tweets %>% mutate(tweet_length = nchar(text))
length_summary <- tweets %>% group_by(candidate) %>% summarise( avg_length = mean(tweet_length), median_length = median(tweet_length), sd_length = sd(tweet_length) )
cat(“=== АНАЛИЗ ДЛИНЫ ТВИТОВ ===”) print(length_summary)
11. TF-IDF АНАЛИЗ (характерные слова)
cat(“=== TF-IDF АНАЛИЗ ===”)
tf_idf_table <- tokens_df %>% count(candidate, word) %>% bind_tf_idf(word, candidate, n) %>% arrange(desc(tf_idf)) %>% group_by(candidate) %>% slice_max(tf_idf, n = 5) %>% ungroup()
print(tf_idf_table)
12. ПРОСТОЙ СЕНТИМЕНТ-АНАЛИЗ
cat(“=== ПРОСТОЙ СЕНТИМЕНТ-АНАЛИЗ ===”)
Создаем простой словарь
positive_dict <- c(“good”, “great”, “best”, “win”, “love”, “strong”, “hope”, “proud”, “thank”, “success”) negative_dict <- c(“bad”, “wrong”, “fail”, “false”, “lie”, “fake”, “stupid”, “problem”, “criticize”, “attack”)
sentiment_df <- tokens_df %>% mutate(sentiment = case_when( word %in% positive_dict ~ 1, word %in% negative_dict ~ -1, TRUE ~ 0 )) %>% filter(sentiment != 0) %>% group_by(candidate) %>% summarise( positive_words = sum(sentiment == 1), negative_words = sum(sentiment == -1), net_sentiment = positive_words - negative_words, sentiment_ratio = positive_words / (positive_words + negative_words) )
print(sentiment_df)
13. ВЫВОДЫ
cat(“=== ОСНОВНЫЕ ВЫВОДЫ ===”) cat(“1. Активность:”, ifelse(popularity_table\(tweets_count[popularity_table\) candidate == “Trump”] > popularity_table\(tweets_count[popularity_table\) candidate == “Clinton”], “Трамп активнее”, “Клинтон активнее”), “”) cat(“2. Вовлеченность:”, ifelse(popularity_table\(engagement_rate[popularity_table\) candidate == “Trump”] > popularity_table\(engagement_rate[popularity_table\) candidate == “Clinton”], “У Трампа выше”, “У Клинтон выше”), “”) cat(“3. Длина твитов:”, ifelse(length_summary\(avg_length[length_summary\) candidate == “Clinton”] > length_summary\(avg_length[length_summary\) candidate == “Trump”], “Клинтон пишет длиннее”, “Трамп пишет длиннее”), “”) cat(“4. Эмоциональность:”, ifelse(sentiment_df\(net_sentiment[sentiment_df\) candidate == “Clinton”] > sentiment_df\(net_sentiment[sentiment_df\) candidate == “Trump”], “У Клинтон позитивнее”, “У Трампа позитивнее”), “”)
library(tidyverse)
ЗАГРУЗКА И ПРЕПРОЦЕССИНГ
tweets <- read.csv(“Yandex.Disk.localized/tweets 2.csv”, stringsAsFactors = FALSE) tweets\(candidate <- ifelse(tweets\) handle == “HillaryClinton”, “Clinton”, “Trump”)
СПИСОК ШТАТОВ США
states <- c(“Alabama”,“Alaska”,“Arizona”,“Arkansas”,“California”,“Colorado”, “Connecticut”,“Delaware”,“Florida”,“Georgia”,“Hawaii”,“Idaho”, “Illinois”,“Indiana”,“Iowa”,“Kansas”,“Kentucky”,“Louisiana”, “Maine”,“Maryland”,“Massachusetts”,“Michigan”,“Minnesota”, “Mississippi”,“Missouri”,“Montana”,“Nebraska”,“Nevada”, “New Hampshire”,“New Jersey”,“New Mexico”,“New York”, “North Carolina”,“North Dakota”,“Ohio”,“Oklahoma”,“Oregon”, “Pennsylvania”,“Rhode Island”,“South Carolina”,“South Dakota”, “Tennessee”,“Texas”,“Utah”,“Vermont”,“Virginia”,“Washington”, “West Virginia”,“Wisconsin”,“Wyoming”)
ПОИСК УПОМИНАНИЙ ШТАТОВ
find_states <- function(text, state_list) { matches <- str_extract_all(text, regex(paste(state_list, collapse=“|”), ignore_case=TRUE)) return(unlist(matches)) }
ДЛЯ КЛИНТОН
clinton_tweets <- tweets %>% filter(candidate == “Clinton”) clinton_states <- unlist(lapply(clinton_tweets$text, find_states, states)) cat(“КЛИНТОН - топ штатов:”) print(sort(table(tolower(clinton_states)), decreasing=TRUE)[1:5])
ДЛЯ ТРАМПА
trump_tweets <- tweets %>% filter(candidate == “Trump”) trump_states <- unlist(lapply(trump_tweets$text, find_states, states)) cat(“- топ штатов:”) print(sort(table(tolower(trump_states)), decreasing=TRUE)[1:5])