Стилометрический анализ авторства «Тихого Дона»

Author

Ксения Войтова

library(tidyverse)
library(ape)
library(philentropy)


#читаем файл и преобразовываем таблицу с частотностями

data <- read.table("table_with_frequencies.txt", 
                      header = TRUE,
                      quote = "\"",
                      check.names = FALSE
                      )
data_t <- t(data) |> as.data.frame()

dist_data <- data_t |> 
  philentropy::distance(method = "cosine", use.row.names = TRUE)

dist_data <- as.dist(1 - dist_data)
hc <- hclust(dist_data)
plot(hc)

#функция для консенсусного дерева

get_tree <- function(df, n_features = 100) {
  # 1. Случайная выборка столбцов
  selected <- df[, sample(ncol(df), size = n_features)]
  
  # 2. Расстояние
  dist_mx <- selected |> 
    philentropy::distance(method = "cosine", 
                          use.row.names = TRUE, 
                          mute.message = TRUE) |> 
    as.dist()
  dist_mx <- 1 - dist_mx
  
  # 3. Кластеризация 
  tr <- hclust(dist_mx) |> as.phylo()
  
  tr
}

#тестовая проверка
test_tree <- get_tree(data_t)
plot(test_tree)

#строим 100 деревьев
set.seed(123)
trees <- map(1:100, ~get_tree(data_t))

cons <- consensus(trees, p = 0.5, rooted = FALSE)
plot(cons, type = "fan")

#извлекаем авторов из названий
authors <- str_extract(cons$tip.label, "^[^_]+")


cols <- tibble(author = authors) |> 
  mutate(color = case_when(
    author == "Шолохов" ~ "#E41A1C",
    author == "Крюков" ~ "#377EB8",
    author == "Булгаков" ~ "#4DAF4A",
    author == "Dubia" ~ "#984EA3",
    author == "Платонов" ~ "#FF7F00",
    author == "Леонов" ~ "#A65628",
    author == "Серафимович" ~ "#F781BF",
    author == "Островский" ~ "#999999",
    author == "Иванов" ~ "#66C2A5",
    author == "Фадеев" ~ "#FC8D62",
    author == "Фурманов" ~ "#8DA0CB",
    author == "Севский" ~ "#E78AC3",
    TRUE ~ "black"
  ))

#график
par(mar = c(0,0,0,0))

plot.phylo(cons, 
           type = "fan",
           tip.color = cols$color,
           font = 2,
           cex = 0.7,
           no.margin = TRUE)

#силы консенсуса на узлах
nodelabels(text = sprintf("%.2f", cons$node.label),
           cex = 0.5,
           frame = "circle",
           bg = "white")

Комментарий

Консенсусное дерево построено на основе 100 деревьев, каждое из которых построено на выборке из 100 случайных слов.

Мы отчетливо видим, что тексты одного автора группируются вместе. Части одного и того же романа группируются с очень высоким консенсусом, например, два тома «Поднятой целины» Шолохова. Все его тексты находятся рядом с «Тихим Доном», тогда как Крюков — на противоположной стороне дерева. Однако устойчивого кластера между произведениями Шолохова и «Тихим Доном» не образуется, что указывает на стилистические различия.