(Гарри Поттера много не бывает)

Что здесь происходит

Для анализа мной была выбрана первая часть из серии романов о Гарри Поттере.

Цели-задачи:

  1. Проследить зависимость между контекстом употребления имени главного героя и сюжетом
  2. Сравнить контекст употребления имен трех главных героев

Текст для анализа был загружен из GitHub - bradleyboehmke/harrypotter - этот пакет предоставляет доступ к полным текстам первых семи книг “Гарри Поттера”. Текст каждого романа получен из Read Vampire Books, частично обработан и готов для анализа.

Что было сделано

Для того, чтобы исследовать контекст упоминания главных героев, извлечем из текста биграммы, начинающиеся на “Гарри”/“Рон”/“Гермиона”. Затем создадим таблицы с результатами для каждого из этих персонажей и визуализируем полученные данные.

#Загружаем необходимые пакеты
library(tidytext)
library(dplyr)
library(ggplot2)
library(tidyr)
library(stringr)
library(harrypotter)
library(wordcloud2)

#philosophers_stone - загруженный файл, содержащий вектор из 17 элементов, каждый из которых - отдельная глава
chapters <- philosophers_stone

data <- tibble(chapter = 1:length(chapters), text = chapters)

#токенизируем текст

tokens <- data %>%
  unnest_tokens(word, text)

#извлекаем биграммы

bigrams <- tokens %>%
  mutate(next_word = lead(word)) %>%
  unite(bigram, word, next_word, sep = " ")

my_bigrams <- bigrams %>%
  filter(str_starts(bigram, "harry |ron |hermione "))

bigram_counts <- my_bigrams %>%
  group_by(chapter, bigram) %>%
  summarise(count = n())

#делим все биграммы по персонажам
bigram_counts_harry <- bigram_counts %>%
  filter(str_starts(bigram, "harry ")) %>%
  split(.$chapter)

bigram_counts_ron <- bigram_counts %>%
  filter(str_starts(bigram, "ron ")) %>%
  split(.$chapter)

bigram_counts_hermione <- bigram_counts %>%
  filter(str_starts(bigram, "hermione ")) %>%
  split(.$chapter)

#функция для сортировки 
process_table <- function(tbl) {
  tbl %>%
    arrange(desc(count))
}

#сортируем каждую таблицу
bigram_counts_harry <- lapply(bigram_counts_harry, process_table)
bigram_counts_ron <- lapply(bigram_counts_ron, process_table)
bigram_counts_hermione <- lapply(bigram_counts_hermione, process_table)


#удаляем стоп-слова

data("stop_words")

remove_stop_words <- function(tbl) {
  tbl %>%
    separate(bigram, c("word1", "word2"), sep = " ") %>%
    filter(!(word1 %in% stop_words$word) & !(word2 %in% stop_words$word)) %>%
    unite(bigram, word1, word2, sep = " ")
}

bigram_counts_harry <- lapply(bigram_counts_harry, remove_stop_words)
bigram_counts_ron <- lapply(bigram_counts_ron, remove_stop_words)
bigram_counts_hermione <- lapply(bigram_counts_hermione, remove_stop_words)

#объединяем все таблицы в одну для Гарри
harry_bigrams <- bind_rows(bigram_counts_harry)

#группируем по биграммам, считаем общее количество, перечисляем номера глав
result_harry <- harry_bigrams %>%
  group_by(bigram) %>%
  summarise(total_count = sum(count), chapters = paste(unique(chapter), collapse = ", ")) %>%
  arrange(desc(total_count)) %>%
  filter(total_count > 2)

#все то же самое для Рона

ron_bigrams <- bind_rows(bigram_counts_ron)

result_ron <- ron_bigrams %>%
  group_by(bigram) %>%
  summarise(total_count = sum(count), chapters = paste(unique(chapter), collapse = ", ")) %>%
  ungroup() %>%
  arrange(desc(total_count))

#оставляем только первые 44 строки, чтобы в облаках слов ниже 
#у всех героев было одинаковое количество слов

top_44_ron <- head(result_ron, 44)

Фрагмент таблицы с наиболее частотными парами слов для Гарри:

Полученные таблицы пригодятся ниже для построения облаков слов. Теперь посмотрим, как по ходу книги менялся контекст для Гарри.

#делаем новую таблицу для графика
plot_data <- result_harry %>%
  separate_rows(chapters, convert = TRUE) %>%
  mutate(bigram = str_replace(bigram, "harry ", "")) %>%
  mutate(bigram = factor(bigram, levels = unique(bigram)))

#строим график
ggplot(plot_data, aes(x = chapters, y = bigram, color = total_count)) +
  geom_point(size = 5) +
  scale_color_gradient(low = "#FFF0F5", high = "darkred") +
  scale_x_continuous(breaks = seq(min(plot_data$chapters), max(plot_data$chapters), by = 1)) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1)) +
  labs(x = "Главы", y = "Биграммы", color = "Количество употреблений")

На графике представлены слова, чаще всего встречающиеся в паре с “Гарри” (по оси x) и номера глав (по оси Y).

Теперь создадим облака слов с наиболее частыми парами слов для каждого из трех главных персонажей:

#создаем облака слов
result_harry$bigram <- gsub("harry ", "", result_harry$bigram)

colors_harry <- rep(c("DarkGreen", "ForestGreen", "OliveDrab", "SeaGreen", "MediumSeaGreen"), length.out = nrow(result_harry))

wordcloud2(data = result_harry, size = 1.2, color = colors, backgroundColor = "Maroon")

top_44_ron$bigram <- gsub("ron ", "", top_44_ron$bigram)

colors_ron <- rep(c("coral", "darkorange", "orangered", "tomato", "peachpuff"), length.out = nrow(top_44_ron))

wordcloud2(data = top_44_ron, size = 1, color = colors_ron, backgroundColor = "darkgreen")

top_44_hermione$bigram <- gsub("hermione ", "", top_44_hermione$bigram)

colors_hermione <- rep(c("yellow", "gold", "lightgoldenrodyellow", "palegoldenrod", "khaki"), length.out = nrow(result_hermione))

wordcloud2(data = result_hermione, size = 1.5, color = colors_hermione, backgroundColor = "SaddleBrown")

Для Гарри:

Для Гермионы:

Для Рона:

Выводы

В результате выявления наиболее часто встречающихся с “Гарри” пар слов можно проследить связь между сюжетом и контекстом упоминания героя. Так, в главах, посвященных финальному противостоянию Гарри с Волан-де-Мортом, глаголы, стоящие после “Гарри”, по смыслу соотвтствуют активным действиям, предпринимаемым героем. По биграммам также можно проследить развитие дружбы между Гарри, Роном и Гермионой - главы, в которых описываются ситуации, сплотившие персонажей, сопровождаются наиболее активным упоминанием их имен в парах.

Облака слов, наиболее часто стоящих за именами героев, позволяют выявить характерные черты последних. например, сли у Гермионы на первом месте оказалось основательное “Granger” (у Рона, напротив, фамилия даже не вошла в топ), то у Рона, неожиданно - muttered. Впрочем, это довольно точно характеризует героя в детстве:)