Будем как Солнце!

Компьютерный анализ стихотворений с тегом «о солнце»

Автор

Полина Дорожкина

Дата публикации

30.11.2024

«Кругом сыро, пасмурно. Мне бы немножко солнца. У тебя есть что-нибудь солнечное?»

— Бараш, серия «Фанерное солнце»

Введение

Осенью важно дарить друг другу тепло. Даже в домашнем задании по R. Данными для анализа стали тексты стихотворений с сайта Культура.рф с тегом «о солнце».

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

Смешарики, серия «Фанерное солнце»

Веб-скрапинг и подготовка таблицы

Загружаем необходимые библиотеки и с помощью инструмента Selector Gadget выбираем нужные уровни:

library(rvest)
library(tidyverse)

url <- "https://www.culture.ru/literature/poems/tag-o-solnce"
html = read_html(url)
poems <- html |> 
  html_elements(".Dx0ke")

Тестируем извелечение нужных нам элементов на 1-ом стихотворении:

poem_1 <- poems[[1]] 

poem_1 |>
  html_element(".nqRUR") |> 
  html_text2()  #автор
[1] "Корней\nЧуковский"
poem_1 |>
  html_element("._6XUM3") |> 
  html_text2()  #название
[1] "Краденое солнце"
poem_1 |>
  html_element("a.ICocV") |> 
  html_attr("href") #ссылка
[1] "/poems/33105/kradenoe-solnce"

Всё работает! Теперь можно приступать к написанию фунции и с помощью map применить её ко всем стихотворениям:

scrape_poems <- function(html_file){
  poems_tbl <- tibble(
    "Автор" = html_file |>
      html_element(".nqRUR") |> 
      html_text2(),
    "Название" = html_file |> 
      html_element("._6XUM3") |> 
      html_text2(),
    "link" = html_file |>
      html_element("a.ICocV") |> 
      html_attr("href")
  )
  poems_tbl
}
poems_tbl <- map_df(poems, scrape_poems)

DT::datatable(poems_tbl, options = list(pageLength = 5))

Теперь добавляем протокол доступа и доменное имя для каждой страницы.

poems_tbl <- poems_tbl |> 
  mutate(link = paste0("https://www.culture.ru", link)) 

get_text <- function(url) {
  read_html(url) |> 
    html_elements(".qPf9A .xZmPc") |> 
    html_text2() |> 
    paste(collapse= " ")
}

sun <- poems_tbl |> 
  mutate(
    "Текст" = map_chr(link, get_text)) |> 
  select(-link)

sun
# A tibble: 18 × 3
   Автор                                Название                           Текст
   <chr>                                <chr>                              <chr>
 1 "Корней\nЧуковский"                  Краденое солнце                    "Сол…
 2 "Михаил\nЛермонтов"                  Солнце                             "Как…
 3 "Максим\nГорький"                    Солнце всходит и заходит           "Пес…
 4 "Игорь\nСеверянин"                   Солнце и море                      "Мор…
 5 "Марина\nЦветаева"                   Два солнца стынут, — о Господи, п… "Два…
 6 "Анна\nАхматова"                     Солнце комнату наполнило           "Сол…
 7 "Владимир\nСолоухин"                 Солнце                             "Сол…
 8 "Евгений Боратынский\n(Баратынский)" С первым солнцем                   "Здр…
 9 "Марина\nЦветаева"                   Луч серебристый                    "Эхо…
10 "Константин\nРоманов"                Мне снилось, что солнце всходило   "Мне…
11 "Максимилиан\nВолошин"               Солнце                             "Свя…
12 "Федор\nСологуб"                     Солнце светлое восходит            "Сол…
13 "Константин\nБальмонт"               Стих о величестве солнца           "Вел…
14 "Николай\nАсеев"                     Как желтые крылья иволги           "Как…
15 "Василий\nЛебедев-Кумач"             Солнце садится                     "Сол…
16 "Александр\nСумароков"               Уже восходит солнце, стада идут в… "Уже…
17 "Евгений\nДолматовский"              Год спокойного солнца              "Это…
18 "Владимир\nСолоухин"                 Уходило солнце в Журавлиху         "Ухо…

C помощью регулярных выражений удаляем из всех текстов дату. Также удаляем “слипшееся” с 3 текстом примечание.

Пример даты в конце стихотворения:

[1] "Солнце комнату наполнило\nПылью жёлтой и сквозной.\nЯ проснулась и припомнила:\nМилый, нынче праздник твой.\nОттого и оснежённая\nДаль за окнами тепла,\nОттого и я, бессонная,\nКак причастница спала.\n1913 г."

Пример примечания перед стихотворением:

[1] "Песня волжских босяков, записанная М. Горьким\nИз пьесы «На дне»Солнце всходит и заходит,\nА в тюрьме моей темно.\nДни и ночи часовые\nСтерегут мое окно.Как хотите стерегите,\nЯ и так не убегу.\nМне и хочется на волю —\nЦепь порвать я не могу.Эх вы, цепи, мои цепи,\nВы железны сторожа,\nНе порвать мне, не разбить вас\nБез булатного ножа."
sunny <- sun |> 
  mutate(Текст = str_remove(Текст, "Песня волжских босяков, записанная М. Горьким\nИз пьесы «На дне»"),
         Текст = str_remove(Текст, "\\d{4} г\\.$"))

Даты на конце больше нет:

[1] "Солнце комнату наполнило\nПылью жёлтой и сквозной.\nЯ проснулась и припомнила:\nМилый, нынче праздник твой.\nОттого и оснежённая\nДаль за окнами тепла,\nОттого и я, бессонная,\nКак причастница спала.\n"

Примечания про “босяков” тоже нет:

[1] "Солнце всходит и заходит,\nА в тюрьме моей темно.\nДни и ночи часовые\nСтерегут мое окно.Как хотите стерегите,\nЯ и так не убегу.\nМне и хочется на волю —\nЦепь порвать я не могу.Эх вы, цепи, мои цепи,\nВы железны сторожа,\nНе порвать мне, не разбить вас\nБез булатного ножа."

Теперь у нас есть таблица, с которой мы можем работать!

Облако слов для частотных существительных

Мой первый вопрос к данным: какие неодушевлённые существительные чаще всего встречаются в стихотворениях о солнце?

Выбор именно неодушевлённых существительных связан с тем, что медведи и крокодилы в стихотворении К.И. Чуковского (Чуковский 1927) были лидерами, что связано с объёмом и сюжетом произведения.

Чтобы подсчитать относительную частотность, мы должны аннотировать тексты стихотворений и отсортировать неодушевлённые существительные по количеству.

library(udpipe)
udpipe_download_model(language = "russian-syntagrus")
           language                                      file_model
1 russian-syntagrus C:/HSE/R/russian-syntagrus-ud-2.5-191206.udpipe
                                                                                                                                       url
1 https://raw.githubusercontent.com/jwijffels/udpipe.models.ud.2.5/master/inst/udpipe-ud-2.5-191206/russian-syntagrus-ud-2.5-191206.udpipe
  download_failed download_message
1           FALSE               OK
russian_syntagrus <- udpipe_load_model(file = "russian-syntagrus-ud-2.5-191206.udpipe")

sunny_annotate <- udpipe_annotate(
  russian_syntagrus, 
  x = sunny$Текст,
  doc_id = sunny$Автор
)

sunny_noun <- as_tibble(sunny_annotate) |> 
  filter(upos == "NOUN") |> 
  filter(str_detect(feats, "Animacy=Inan")) |> 
  select(token, lemma, upos) |> 
  count(lemma, sort = TRUE)

DT::datatable(sunny_noun, options = list(pageLength = 10))

Приступаем к созданию облака слов в форме… звезды, конечно!

library(wordcloud2)
wordcloud2(sunny_noun, size = 0.7, shape = 'star', color="gold", backgroundColor="black")

Целесообразно было бы исключить очевидно лидирующее и без подсчётов слово “солнце”, однако мне захотелось поделиться с вами такой “звездой по имени Солнце”.

library(tidyverse)
sunny_without_sun <- sunny_noun |> 
  filter(lemma != "солнце")
DT::datatable(sunny_without_sun, options = list(pageLength = 10)) 

Вывод: частотные слова позволяют нам составить представление о поэтических образах, связанных с солнцем.

Подупункты вывода

  • Антонимические пары. Среди существительных чётко можно выделить антонимичные пары с примерно равной частотностью: небо-земля (9-7), день-ночь(8-9), мрак-свет (4-4).

  • Группы образов. Распределяя частотные существительные по тематическим группам, получаются следующие:

  1. стихии и природа: огонь, дерево, море, болото, ветка, куст, лужайка, мороз, снег, вода;
  2. цикличность и время: вечер, год, весна, время, час;
  3. музыкальная: песня, гармонь, скрипка;
  4. покой и гармония: сердце, любовь, душа, мир.
  • Ассоциации и противоположности. Среди слов можно увидеть как образы-ассоциации с солнцем как чем-то светлым и горячим: огонь, сердце, любовь, свет, луч; так и образы-противоположности, связанные с чем-то тёмным и холодным: холод, мороз, мрак, тень.

TF-IDF

Мой второй вопрос к данным: что нам может сказать об авторах стихотворений подсчёт TF-IDF?

Для этого мы удаляем стоп-слова и соотносим слова из стихотворений с авторами.

library(stopwords)
library(tidytext)

stop_words <- stopwords("ru")
sunny_clean <- as_tibble(sunny_annotate)  |>  
  filter(!lemma %in% stop_words)

author_sunny <- sunny_clean |> 
  filter(upos %in% c("NOUN", "VERB", "ADJ", "PROPN")) |> 
  select(doc_id, lemma) |> 
  rename(author = doc_id, word = lemma) |> 
  mutate(word = tolower(word))

DT::datatable(author_sunny, options = list(pageLength = 10))

Теперь считаем TF-IDF.

author_word_counts <- author_sunny  |> 
  count(author, word, sort = T) |> 
  ungroup()

total_counts <- author_word_counts |> 
  group_by(author) |> 
  summarise(total = sum(n))

author_word_tf <- author_word_counts |> 
  left_join(total_counts)|> 
  mutate(tf = round((n / total), 5))

author_word_tfidf <- author_word_tf |> 
  bind_tf_idf(word, author, n) |> 
  arrange(desc(tf_idf))

DT::datatable(author_word_tfidf, options = list(pageLength = 10))

Строим график, на котором будут представлены по 10 слов с самым высоким TF-IDF для каждого из 16 авторов.

library(showtext)
library(ggplot2)

font_add_google("Kanit", "kanit") 
showtext_auto()
  
  author_word_tfidf |> 
  arrange(-tf_idf) |> 
  mutate(tf_idf = round(tf_idf, 3)) |> 
  group_by(author) |> 
  filter(row_number() <= 10) |> 
  ungroup() |> 
  ggplot(aes(reorder_within(word, tf_idf, author), tf_idf, fill = author)) +
  scale_fill_manual(values = c(
    "Александр Сумароков" = "#ffae14",
    "Анна Ахматова" = "#ffb31e",
    "Василий Лебедев-Кумач" = "#ffb728",
    "Владимир Солоухин" = "#ffbc31",
    "Евгений Боратынский (Баратынский)" = "#ffc03b",
    "Евгений Долматовский" = "#ffc545",
    "Игорь Северянин" = "#ffca4f",
    "Константин Бальмонт" = "#ffce59",
    "Константин Романов" = "#ffd362",
    "Корней Чуковский" = "#ffd76c",
    "Максим Горький" = "#ffdc76",
    "Максимилиан Волошин" = "#ffe180",
    "Марина Цветаева" = "#ffe58a",
    "Михаил Лермонтов" = "#ffea93",
    "Николай Асеев" = "#ffee9d",
    "Федор Сологуб" = "#fff3a7"
  )) +
  theme_minimal(base_size = 16) +
  geom_col(show.legend = F) +
  geom_text(aes(label = tf_idf),
            color = "darkred",
            size = 4,
            hjust = 1.2) +
  labs(title = "TF-IDF солнечных стихотворений", 
       x = NULL, 
       y = NULL) +
  theme(
    plot.title = element_text(size = 20, color = "darkred", face = "bold", hjust = 0.5), 
    axis.text.x = element_text(size = 14, color = "darkred"),
    axis.text.y = element_text(size = 11, color = "darkred"),
    strip.text = element_text(size = 16, color = "darkred", face = "bold"),
    plot.title.position = "plot") +
  facet_wrap(~author, scales = "free") +
  scale_x_reordered() +
  coord_flip()

Выводы: подсчёт TF-IDF помогает выявить ключевые слова, которые могут дать информацию о содержании стихотворения или о его авторе.

Подупункты вывода

  • Содержание. Важные для текста слова помогают догадаться о содержании этого текста. Например, даже не видя имя автора, мы с лёгкостью узнаём “Краденое солнце” К.И. Чуковского (Чуковский 1927) по словам “медведь”, “медвежата”, крокодил”, “плакать”, “солнышко”. Мы можем предположить содержание и незнакомого произведения. Так в “Мне снилось, что солнце всходило” К. Романова (Романов, б. д.) слова “весна”, “лёд”, “снег”, “мороз”, “всходить” дают ассоциацию с первыми солнечными лучами, приходом весны (что действительно совпадает с содержанием стихотворения).

  • Автор. Ключевые слова одного текста могут помочь сделать некоторые выводы об авторе или даже отгадать его. Эпоху А. Сумарокова (Сумароков, б. д.) выдают неполногласие “драгой”, устаревшие “воспрещать”, прилагательное “любезный” и пасторальная “пастушка”. Предположить авторство по ключевым словам также возможно: “цепь”, рвать”, “разбить”, “железный”, “булатный” стихотворения М. Горького (Горький, б. д.) или “слабый”, “оживать”, “дева”, “обещать” стихотворения М.Ю. Лермонтова (Лермонтов 1832).

  • Ограничения метода. Результат также отражает одно из ограничений метода TF-IDF, а именно чувствительность к длине данных. Результаты коротких стихотворений не могут дать той же полноты выводов, что и длинные тексты. Например, в стихотворении А.Ахматовой (Ахматова 1913) у 14 из 17 слов одинаковые значения TF-IDF, у Е.Баратынского (Баратынский, б. д.) - у 20 из 31.

использованная литература

Ахматова, Анна. 1913. Солнце комнату наполнило.
Баратынский, Евгений. б. д. С первым солнцем.
Горький, Максим. б. д. Солнце всходит и заходит.
Лермонтов, Михаил. 1832. Солнце.
Романов, Константин. б. д. Мне снилось, что солнце всходило.
Сумароков, Александр. б. д. Уже восходит солнце, стада идут в луга.
Чуковский, Корней. 1927. Краденое солнце.