Признанный пародийным в “Средневековом” научном сообществе роман “Окассен и Николетт” на уровне образном и лексическом разительно отличается от романов бретонского цикла, не рассматриваемых через призму устройства Средневековой пародии. Все, что ниже, было создано автором с большим трудом, но с большим интересом и пытливостью, и с целью: лишь попытаться приблизиться к пониманию устройства языка Средневековой пародии. Цель эта, очевидно, не была достигнута, полученные результаты слишком незамысловаты и логичны, но поставленную цель автор еще долго будет преследовать.
“Характерно, что самая маленькая средневековая пародия всегда построена так, как если бы она была обломком целого и единого комического мира”
Предобработка текста
Устанавливаем нужные библиотеки, очищаем текст от знаков препинания и делим на токены. Изначально тексты были в pdf формате, они были конвертированы в txt формат, далее прочитаны с помощью библиотеки readtext:
Также задачей было лемматизировать тексты. В udpipe даже нашлась модель обработки текста на старофранцузском языке (old_french-srcmf), однако она не сработала (по неопознанным причинам) так, как нужно (в колонке lemma все показатели были NA).
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr 1.1.4 ✔ purrr 1.0.4
✔ forcats 1.0.0 ✔ readr 2.1.5
✔ ggplot2 3.5.1 ✔ tibble 3.2.1
✔ lubridate 1.9.4 ✔ tidyr 1.3.1
── 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
library(tidytext)library(tokenizers)#library(udpipe)library(ggplot2)data1 <-readtext("aucassin.txt")data2 <-readtext("ErecKu.txt")data3 <-readtext("PercevalKu.txt")text1 <- data1$texttext2 <- data2$texttext3 <- data3$textget_clean_texts <-function(text) {#text <- str_replace_all(text,"^M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$"," ") text <-str_replace_all(str_to_lower(text), "\n", " ") text <-str_replace_all(text,"\t", " ") text <-str_replace_all(text, "[:digit:]", " ") text <-str_replace_all(text, "’", "'") text <-str_replace_all(text, "[[:punct:]]", "") text <-str_replace_all(text, "\\s+", " ") text <-str_replace_all(text, "txmbfmcorpusorg", " ") text <-str_replace_all(text, "\fbase de français médiéval aucassin et nicolette", " ") text <-str_replace_all(text,"\fbase de français médiéval chrétien de troyes erec et enide", " ") text <-str_replace_all(text, "\fbase de français médiéval chrétien de troyes conte du graal perceval", " ") }listoftexts <-c(text1,text2,text3)cleantexts <-map(listoftexts, get_clean_texts) #map (есть функция, написанная для одного текста (очистка и map нужен для того, чтобы применить очистку ко всем текстам))textnames <-c("Aucassin et Nicolette","Perceval","Erec et Enide") #названия текстов для первой колонки (с использованием вектора, вектор это создание списка)roman <-tibble("textname"=textnames,"text"=cleantexts) #таблица с названиями текстов и текстами text_tokens <- roman |>#таблица с токенамиunnest_tokens("word", "text") #только выделение токенов
Расчет относительной частоты встречаемости
1 шаг — раcчет частоты встречаемости
2 шаг — расчет количества всех слов в каждом документе
3 шаг — объеденение таблиц с этими показателями
4 шаг — расчет относительной частоты встречаемости (частота встречаемости слова / количество всех слов в документе)
roman_word_counts <- text_tokens |>#частота встречаемости словcount(textname, word, sort =TRUE) #функция count считает тут количество токенов (сортировка по частотности): и получаем какой токен в каком текстеtotal_counts <- roman_word_counts |>group_by(textname) |>#группирует по указанной колонке (добавляем знак пайпа чтобы он знал с чем именно работать дальше: с группированным текстом)summarise(total =sum(as.integer(n))) #для получения соотношения одного слова на весь текст #as integer для того, чтобы показать что n превращаем именно в цифруroman_word_counts <- roman_word_counts |>#перезаписываем чтобы на следующем шаге работать с таблицей вместо того чтобы создавать новую таблицу (вместо этого одну из имеющихся перезаписываем)left_join(total_counts) # к roman_word_counts присоединяем тоутал каунтс
Joining with `by = join_by(textname)`
roman_word_thefrequency <- roman_word_counts |>mutate(thefrequency =round((n / total), 5)) #mutate для вычисления (в данном случае для деления: меньшее делим на большее; процент/доля)head(roman_word_thefrequency)
# A tibble: 6 × 5
textname word n total thefrequency
<chr> <chr> <int> <int> <dbl>
1 Erec et Enide et 2636 53004 0.0497
2 Perceval et 1881 39880 0.0472
3 Erec et Enide que 1464 53004 0.0276
4 Erec et Enide de 1365 53004 0.0258
5 Erec et Enide li 1213 53004 0.0229
6 Perceval de 1169 39880 0.0293
TF IDF
roman_word_thefrequencyidf <- roman_word_thefrequency |>#(обозначаем что конкретно с этой таблицей работаем с частотнотсью)bind_tf_idf(word, textname, n) #idf=inverse document frequency (важность какого-то слова для коллекции текстов)head(roman_word_thefrequencyidf)
# A tibble: 6 × 8
textname word n total thefrequency tf idf tf_idf
<chr> <chr> <int> <int> <dbl> <dbl> <dbl> <dbl>
1 Erec et Enide et 2636 53004 0.0497 0.0497 0 0
2 Perceval et 1881 39880 0.0472 0.0472 0 0
3 Erec et Enide que 1464 53004 0.0276 0.0276 0 0
4 Erec et Enide de 1365 53004 0.0258 0.0258 0 0
5 Erec et Enide li 1213 53004 0.0229 0.0229 0 0
6 Perceval de 1169 39880 0.0293 0.0293 0 0
Визуализация
Визуализированные слова с наиболее высокими показателями TF IDF по каждому из документов.
roman_word_thefrequencyidf |>arrange(-tf_idf) |>#сортировкаgroup_by(textname) |>#группирует по названиям документаtop_n(20) |>ungroup() |>#без этого сломаетсяggplot(aes(reorder_within(word, tf_idf, textname), tf_idf, fill = textname)) +geom_col(show.legend = F) +labs(x =NULL, y ="tf-idf") +facet_wrap(~textname, scales ="free") +scale_x_reordered() +coord_flip()
Selecting by tf_idf
Постскриптум
Результаты TF IDF по каждому из документов предложили не так много вариантов для интерпретаций. Возможно, и даже абсолютно точно стоило бы улучшить качество предобработки текста (убрать стоп-слова, добиться лемматизации слов на старофранцузском и др.)