Об авторе

Для анализа я выбрала сборник рассказов “Beasts and Super-Beasts” английского писателя Гектора Хью Манро, использовавшего псевдоним Саки. Источником будет Wikisource.

“Beasts and Super-Beasts” – это последний сборник рассказов автора, опубликованный незадолго до его гибели в Первой мировой войне. В некоторый степени является пародией на “Man and Superman” Бернарда Шоу. Саки был известен своим цинизмом и черным юмором, и в рассказах этого сборника он изображает призраков, обман, жуткие пейзажи и демонических детей, создавая тревожную атмосферу, полную неожиданных поворотов.

Манро воспитывался под плотным и душным надзором двух своих теток, поэтому в его рассказах тетушки разных персонажей — весьма гнусные и противные особы. Дети часто обводят их вокруг пальца, запугиванием или манипуляциями добиваясь желаемого.

Одним из моих интересов в работе над данным сборником является описание детей и взрослых в рассказах.

Начало работы

Итак, загружаем все нужные в работе библиотеки и отдаем ссылку:

library(tidyverse)
library(tidytext)
library(rvest)
library(udpipe)
library(tokenizers)
library(wordcloud2)
library(igraph)
library(ggraph)

html <- read_html("https://en.wikisource.org/wiki/Beasts_and_Super-Beasts")

Я хочу создать тиббл, в котором будут столбцы с названием рассказа и ссылкой на него. Сначала создаю отдельные столбцы:

title <- html %>%
  html_elements("#mw-content-text li a") %>%
  html_text2() %>%
  as_tibble_col(column_name = "title")

stable_url <- html %>%
  html_elements("#mw-content-text li a") %>%
  html_attr("href") %>%
  paste0("https://en.wikisource.org/", .) %>%
  as_tibble_col(column_name = "stable_url")

А затем сшиваю их, удаляя рассказ, который мне не нужен, и забираю ссылки на оставшиеся:

beasts_tbl <- cbind(title, stable_url) %>%
  filter(row_number() != 31)

urls <- beasts_tbl %>%
  pull(stable_url)

Загрузка рассказов

Я хочу записать каждый рассказ в отдельный txt-файл специально созданной папки. Пишу две функции, которые смогут дать мне название и текст каждого рассказа:

get_title <- function(url) {
  read_html(url) %>% 
    html_element("#header_section_text") %>%
    html_text2()
}

get_text <- function(url) {
  read_html(url) %>% 
    html_elements("p") %>%
    html_text2() %>%
    paste(collapse = "\n")
}

А теперь запускаю цикл, который соберет рассказы и запишет их в нужную папку под соответствующим названием:

for (url in urls) {
  path_to_file <- get_title(url) %>%
    paste0("./Munro/", .) %>%
    paste0(., ".txt")
  # файл будет называться так же как и рассказ
  text <- get_text(url) %>%
    str_remove(., pattern = "From Beasts and Super-Beasts\n") # убираю из текста не нужную мне отсылку на сборник
  write_lines(text, file = path_to_file)
}

Лемматизация

Для лемматизации я буду использовать модель english-gum.

udpipe_download_model(language = "english-gum")

eng_gum <- udpipe_load_model(file = "english-gum-ud-2.5-191206.udpipe")

Захожу в свою папку с рассказами, собираю список файлов для анализа и создаю новый оператор, который мне вскоре понадобится.

stories <- list.files(path = "./Munro", pattern = ".txt")

`%notin%` <- Negate(`%in%`)

Создаю функцию для аннотирования и применяю ее к файлам из своего списка:

get_ann <- function(story) {
  read_file(story) %>%
  udpipe_annotate(eng_gum, ., doc_id = str_remove(story, pattern = ".txt")) %>%
  # doc_id будет равен названию рассказа
  as_tibble() %>%
  select(-sentence, -paragraph_id)
}

beasts_anns <- map_df(stories, get_ann)

В результате получается вот такой тиббл:

doc_id sentence_id token_id token lemma upos xpos feats head_token_id dep_rel deps misc
A Defensive Diamond 1 1 Treddleford Treddleford PROPN NNP Number=Sing 2 nsubj NA NA
A Defensive Diamond 1 2 sat sit VERB VBD Mood=Ind|Tense=Past|VerbForm=Fin 0 root NA NA
A Defensive Diamond 1 3 in in ADP IN NA 6 case NA NA
A Defensive Diamond 1 4 an a DET DT Definite=Ind|PronType=Art 6 det NA NA
A Defensive Diamond 1 5 easeful easeful ADJ JJ Degree=Pos 6 amod NA NA
A Defensive Diamond 1 6 arm-chair arm-chair NOUN NN Number=Sing 2 obl NA NA
A Defensive Diamond 1 7 in in ADP IN NA 8 case NA NA
A Defensive Diamond 1 8 front front NOUN NN Number=Sing 2 obl NA NA
A Defensive Diamond 1 9 of of ADP IN NA 12 case NA NA
A Defensive Diamond 1 10 a a DET DT Definite=Ind|PronType=Art 12 det NA NA
A Defensive Diamond 1 11 slumberous slumberous ADJ JJ Degree=Pos 12 amod NA NA
A Defensive Diamond 1 12 fire fire NOUN NN Number=Sing 8 nmod NA SpaceAfter=No

Распределение частей речи

Я хочу узнать количественное соотношение частей речи, использованных в сборнике, и создать столбиковую диаграмму для представления результата:

beasts_anns %>%
  group_by(upos) %>% 
  count() %>% 
  filter(upos %notin% c("PUNCT", "X", "SYM")) %>% # здесь и пригождается новый оператор
  ggplot(aes(x = reorder(upos, n), y = n, fill = upos)) +
  geom_bar(stat = "identity", show.legend = F) +
  labs(x = "Part of Speech", title = "Parts of Speech", subtitle = "in Short Stories by H.H. Munro") +
  coord_flip() +
  theme_bw()

My-bar

Как выясняется, существительные сильно доминируют. Глаголы отстают от них больше чем на 3000 единиц. Прилагательных и наречий не так много, как мне казалось при прочтении рассказов. Числительных не слишком много, и при более близком изучении становится понятно, что большая часть из них обозначает возраст персонажей-детей. Самый большой - пятнадцать лет.

Наиболее частотные леммы

Какие леммы чаще всего встречаются в сборнике? Давайте выясним, используя следующий код:

top_lemmas <- beasts_anns %>%
  filter(upos %in% c("NOUN", "ADJ")) %>%
  # оставляю только прилагательные и существительные
  count(lemma) %>% 
  arrange(-n) %>%
  head(100)

Для наглядности сразу же создаю облако:

pal <- c('#522E75', '#107050', '#D50B53', '#05328E', '#F05837')
shuffled_pal <- sample(pal)

wordcloud2(top_lemmas,
           size = 0.7,
           fontFamily = 'Monotype Corsiva',
           color = rep(shuffled_pal, 20), backgroundColor = '#E6EFF3',
           shape = 'diamond',
           minRotation = -pi/8, maxRotation = -pi/8)

Совместная встречаемость слов

Теперь посмотрим, какие слова чаще встречаются в одном предложении:

x <-  subset(beasts_anns, upos %in% c("NOUN", "ADJ"))

cooc <- x %>%
  cooccurrence(term = "lemma", group = c("doc_id", "sentence_id")) %>%
  head(50)

wordnetwork <- graph_from_data_frame(cooc)

ggraph(wordnetwork, layout = "fr") +
  geom_edge_link(aes(width = cooc, edge_alpha = cooc), edge_colour = "lightgreen") +
  geom_node_text(aes(label = name), col = "darkblue", size = 4) +
  theme(legend.position = "none") +
  labs(title = "Сooccurrence", subtitle = "Adjectives and Nouns")

Untitled

Ожидаемо, в центре внимания у нас дети, взаимодействующие с молодыми и не очень людьми. Примечательно, что ниточка к story тянется именно от girl. По моим наблюдениям, именно девочки сочиняют истории, часто загадочные или жутковатые, в рассказах Саки. Отдельно можно заметить слово aunt, встречающееся вместе с gooseberry garden (предполагаю, благодаря рассказу “The Lumber Room”) и garden party – любимые вещи тетушек Манро.

Кто такие beasts и с чем их едят :)

Напоследок я решила взглянуть на биграммы со словом beast, не зря же оно вынесено в название сборника. Собираю текст всех историй в один тиббл, делю их на биграммы и с помощью str_detect() выбираю только те, в которых есть интересующее слово:

get_story <- function(story) {
  read_file(story) %>%
    as_tibble()
}

beasts_ngrams <- map_df(stories, get_story) %>%
  unnest_tokens(output = ngram, input = value, token = "ngrams", n = 2) %>%
  filter(str_detect(ngram, pattern = "beast") == T) %>%
  count(ngram) %>%
  arrange(-n)

Как выяснилось, beasts часто little, и, судя по общему настроению сборника, являются хитрыми детьми, любящими поиздеваться над некоторыми взрослыми.

Литература

Beasts and Super-Beasts. (2023, February 25). In Wikisource . Retrieved 21:37, December 7, 2023, from https://en.wikisource.org/w/index.php?title=Beasts_and_Super-Beasts&oldid=13024323