library(rvest)
library(tidyverse)
<- "https://www.culture.ru/literature/poems/tag-o-solnce"
url = read_html(url)
html <- html |>
poems html_elements(".Dx0ke")
Будем как Солнце!
Компьютерный анализ стихотворений с тегом «о солнце»
«Кругом сыро, пасмурно. Мне бы немножко солнца. У тебя есть что-нибудь солнечное?»
— Бараш, серия «Фанерное солнце»
Введение
Осенью важно дарить друг другу тепло. Даже в домашнем задании по R. Данными для анализа стали тексты стихотворений с сайта Культура.рф с тегом «о солнце».
Скачать готовую таблицу для собственных исследований можно здесь.
Веб-скрапинг и подготовка таблицы
Загружаем необходимые библиотеки и с помощью инструмента Selector Gadget выбираем нужные уровни:
Тестируем извелечение нужных нам элементов на 1-ом стихотворении:
<- poems[[1]]
poem_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 применить её ко всем стихотворениям:
<- function(html_file){
scrape_poems <- tibble(
poems_tbl "Автор" = 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
}<- map_df(poems, scrape_poems)
poems_tbl
::datatable(poems_tbl, options = list(pageLength = 5)) DT
Теперь добавляем протокол доступа и доменное имя для каждой страницы.
<- poems_tbl |>
poems_tbl mutate(link = paste0("https://www.culture.ru", link))
<- function(url) {
get_text read_html(url) |>
html_elements(".qPf9A .xZmPc") |>
html_text2() |>
paste(collapse= " ")
}
<- poems_tbl |>
sun 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Без булатного ножа."
<- sun |>
sunny 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
<- udpipe_load_model(file = "russian-syntagrus-ud-2.5-191206.udpipe")
russian_syntagrus
<- udpipe_annotate(
sunny_annotate
russian_syntagrus, x = sunny$Текст,
doc_id = sunny$Автор
)
<- as_tibble(sunny_annotate) |>
sunny_noun filter(upos == "NOUN") |>
filter(str_detect(feats, "Animacy=Inan")) |>
select(token, lemma, upos) |>
count(lemma, sort = TRUE)
::datatable(sunny_noun, options = list(pageLength = 10)) DT
Приступаем к созданию облака слов в форме… звезды, конечно!
library(wordcloud2)
wordcloud2(sunny_noun, size = 0.7, shape = 'star', color="gold", backgroundColor="black")
Целесообразно было бы исключить очевидно лидирующее и без подсчётов слово “солнце”, однако мне захотелось поделиться с вами такой “звездой по имени Солнце”.
library(tidyverse)
<- sunny_noun |>
sunny_without_sun filter(lemma != "солнце")
::datatable(sunny_without_sun, options = list(pageLength = 10)) DT
Вывод: частотные слова позволяют нам составить представление о поэтических образах, связанных с солнцем.
Подупункты вывода
Антонимические пары. Среди существительных чётко можно выделить антонимичные пары с примерно равной частотностью: небо-земля (9-7), день-ночь(8-9), мрак-свет (4-4).
Группы образов. Распределяя частотные существительные по тематическим группам, получаются следующие:
- стихии и природа: огонь, дерево, море, болото, ветка, куст, лужайка, мороз, снег, вода;
- цикличность и время: вечер, год, весна, время, час;
- музыкальная: песня, гармонь, скрипка;
- покой и гармония: сердце, любовь, душа, мир.
- Ассоциации и противоположности. Среди слов можно увидеть как образы-ассоциации с солнцем как чем-то светлым и горячим: огонь, сердце, любовь, свет, луч; так и образы-противоположности, связанные с чем-то тёмным и холодным: холод, мороз, мрак, тень.
TF-IDF
Мой второй вопрос к данным: что нам может сказать об авторах стихотворений подсчёт TF-IDF?
Для этого мы удаляем стоп-слова и соотносим слова из стихотворений с авторами.
library(stopwords)
library(tidytext)
<- stopwords("ru")
stop_words <- as_tibble(sunny_annotate) |>
sunny_clean filter(!lemma %in% stop_words)
<- sunny_clean |>
author_sunny filter(upos %in% c("NOUN", "VERB", "ADJ", "PROPN")) |>
select(doc_id, lemma) |>
rename(author = doc_id, word = lemma) |>
mutate(word = tolower(word))
::datatable(author_sunny, options = list(pageLength = 10)) DT
Теперь считаем TF-IDF.
<- author_sunny |>
author_word_counts count(author, word, sort = T) |>
ungroup()
<- author_word_counts |>
total_counts group_by(author) |>
summarise(total = sum(n))
<- author_word_counts |>
author_word_tf left_join(total_counts)|>
mutate(tf = round((n / total), 5))
<- author_word_tf |>
author_word_tfidf bind_tf_idf(word, author, n) |>
arrange(desc(tf_idf))
::datatable(author_word_tfidf, options = list(pageLength = 10)) DT
Строим график, на котором будут представлены по 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.