Визуализация композиции книги при помощи сетей

на материале первых двух книг по вселенной ‘Ведьмака’

Автор

Пирогова Виктория

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

6.03.2025

Введение

Цикл книг Анджея Сапковского в жанре фэнтези начал выходить еще в прошлом веке. Сегодня вселенная Neverland получила экранизацию от Netflix, а также две полноценные игры от студии CD project Red(вторая игра должна должна выйти уже в 2025 году).

Многие присоединились к любителям этой вселенной именно после выхода вышеперечисленных цифровых проектов. Однако, игры и экранизация не отражают изначальной концепции книг Сапоквского. Так, первые две книги представляют собой сборники рассказов, в центре которых находится главный герой – Геральт из Ривии.

Чтобы визуализировать нарративы первых двух книг, а также постепенное образование “скелета” (основных действующих лиц, которые будут переходить из книги в книгу, способствуя образованию линейного сюжета), обратимся к сетевому анализу, а именно, попробуем представить диалоги первый двух книг в виде графа, используя этот датасет

Библиотеки

library(tidyverse)
library(ggraph)
library(igraph)
library(extrafont) # для импорта шрифта
library(paletteer) # для импорта кастомной палитры

Сетевой анализ

Предварительно получаем две таблички, которые затем превратим в графы. Этот датасет содержит информацию о репликах героев в формате source/target. Из аттрибутов: имена вершин и вес ребер. Графы будyт ненаправленными.

#в случае если есть проблема с нахождением шрифта
#font_import()
#loadfonts(device = "win")

witcher <- read.csv("witcher_network.csv")
witcher_1 <- witcher|>
  rename(person_1 = Source, person_2 = Target)|>
  select(-X, -Type)|>
  filter(book == 1)

witcher_2 <- witcher|>
  rename(person_1 = Source, person_2 = Target)|>
  select(-X, -Type)|>
  filter(book == 2)

Табличка для первой книги:

Табличка для второй книги:

Трансформируем датафреймы в графы. Добавляем аттрибут “degree” (отражает количество взаимодействий каждой вершины с другими)

witcher_1_g <- graph_from_data_frame(witcher_1)
witcher_2_g <- graph_from_data_frame(witcher_2)

d_1 <- as.numeric(degree(witcher_1_g))
V(witcher_1_g)$degree <- d_1
d_2 <- as.numeric(degree(witcher_2_g))
V(witcher_2_g)$degree <- d_2

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

Цветом обозначены имена нескольких героев, которые являются сюжетно важными для всех книг вселенной “Ведьмака”: самого Геральда, Цири, Лютика, Йеннифер и Трисс. Стояла задача узнать, насколько они важны в первых двух книгах.

#создаем доп. столбец, куда зановим "главность".
main_char_tbl <- tibble(name = V(witcher_1_g)$name) |> 
  mutate(char_name = case_when(
    str_detect(name, "(Geralt|Ciri|Triss|Yennefer|Dandelion)") ~ "main",
    .default = "not_main"))

V(witcher_1_g)$char_name <- main_char_tbl$char_name

#загружаем кастомную палитру
cols <- paletteer_d("DresdenColor::colddays")

ggraph(witcher_1_g, layout = "lgl") + #задаем раскладку вручную
  geom_edge_link(color = "grey70") +
  geom_node_point(aes(size = degree, 
                      fill = char_name),#хотим, чтобы контур был одним цветом, а заливка другим
                  shape = 21, # это кружки с заливкой
                  color = "black",
                  show.legend = F) +
  geom_node_text(aes(label = name), nudge_y = 1) +
  scale_size(guide = 'none') + #убираем degree в легенде(guide это легенда)
  scale_fill_manual(values = cols) +
  set_graph_style(
    family = "Candara",
    face = "plain",
    size = 11,
    text_size = 9,
    text_colour = "black")

Уже было сказано, что первая книга представляет собой сборник рассказов. Теперь, когда у нас есть визуализация, важно отметить, что второстепенные персонажи из разных рассказов не связаны, и каждая глава образуем отдельную историю. Связующим звеном между ними является Геральт. На этом графе видно несколько кластеров – это и есть отдельные рассказы.

Обратим внимание на то, что в первой книге еще нет Цириллы(Цири). Все потому что на момент начала повествования она еще не родилась.Первая книга – предыстория ее семьи (и каким боком там появился Геральд).

Теперь визуализируем вторую книгу и посмотрим, что изменилось.

#создаем доп. столбец, куда зановим "главность".
main_char_tbl_2 <- tibble(name = V(witcher_2_g)$name) |> 
  mutate(char_name_2 = case_when(
    str_detect(name, "(Geralt|Ciri|Triss|Yennefer|Dandelion)") ~ "main",
    .default = "not_main"))

V(witcher_2_g)$char_name_2 <- main_char_tbl_2$char_name_2

#загружаем кастомную палитру
cols <- paletteer_d("DresdenColor::colddays")

ggraph(witcher_2_g, layout = "auto") +
  geom_edge_link(color = "grey70") +
  geom_node_point(aes(size = degree, 
                      fill = char_name),#хотим, чтобы контур был одним цветом, а заливка другим
                  shape = 21, # это кружки с заливкой
                  color = "black",
                  show.legend = F) +
  geom_node_text(aes(label = name), nudge_y = 1) +
  scale_size(guide = 'none') + #убираем degree в легенде(guide это легенда)
  scale_fill_manual(values = cols) +
  set_graph_style(
    family = "Candara", #симпатичный шрифт
    face = "plain",
    size = 11,
    text_size = 9,
    text_colour = "black")

Во второй книге сюжет становится более линейным. Повествование перестает скакать от одного момента жизни Геральда к другому, мы видим отрывки, но эти отрывки связаны линейно. Появляются два важных героя, которых мы не видели на предыдущем графе: Лютик(Dandelion) и Цири(Ciri). Если отражать на графе вес ребер, то больше всего коммуникаций будет у двоек Геральд/Лютик и Геральд/Цири. Все потому что герои путешествуют вместе. А вот Трисс(Triss) на момент второй книги все еще находится далеко от основного повествования.

В итоге получился достаточно интересный анализ через визуализацию сетей. Сапковский не думал, что сборник рассказов превратится в полноценную вселенную с линейным сюжетом. Это видно по количеству первонажей в первой книге, а также по его резкому сокращению уже во второй (обе книги примерно в 300 страниц, поэтому говорить о несоразмерности не приходится).