Анализ твитов Хиллари Клинтон и Дональда Трампа во время президентских дебатов 2016 года

Author

Ярослав Курганов

Published

December 16, 2025

Интернет-исследование одних из самых скандальных выборов XXI века

Хиллари Клинтон призвала Трампа защитить США от русских — РБК

О чем это исследование? В 2016-м году прошли выборы президента США, победу в которых одержал текущий президент страны Дональд Трамп. В этом тексте мы проанализируем твиты Трампа (@realDonaldTrump) и его оппонентки из Демократической партии Хиллари Клинтон (@HillaryClinton).

Импортируем файл

Code
library(dplyr)

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"))
Клинтон: 3226
Code
cat("\nТрамп:", sum(tweets$candidate == "Trump"))

Трамп: 3218
Code
cat("\nВсего:", nrow(tweets))

Всего: 6444
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-го года уже давно известно, кто в той гонке одержал победу – но в этом исследовании через открытые данные я выделю основные аспекты ведения соцсетей республиканским кандидатом, которые помогли ему получить поддержку электората. Trump’s twisted tweets add to his popularity

А пока что токенизируем наши данные.

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