library(tidyverse)
library(ggraph)
library(igraph)
library(extrafont) # для импорта шрифта
library(paletteer) # для импорта кастомной палитры
Визуализация композиции книги при помощи сетей
на материале первых двух книг по вселенной ‘Ведьмака’
Введение
Цикл книг Анджея Сапковского в жанре фэнтези начал выходить еще в прошлом веке. Сегодня вселенная Neverland получила экранизацию от Netflix, а также две полноценные игры от студии CD project Red(вторая игра должна должна выйти уже в 2025 году).
Многие присоединились к любителям этой вселенной именно после выхода вышеперечисленных цифровых проектов. Однако, игры и экранизация не отражают изначальной концепции книг Сапоквского. Так, первые две книги представляют собой сборники рассказов, в центре которых находится главный герой – Геральт из Ривии.
Чтобы визуализировать нарративы первых двух книг, а также постепенное образование “скелета” (основных действующих лиц, которые будут переходить из книги в книгу, способствуя образованию линейного сюжета), обратимся к сетевому анализу, а именно, попробуем представить диалоги первый двух книг в виде графа, используя этот датасет
Библиотеки
Сетевой анализ
Предварительно получаем две таблички, которые затем превратим в графы. Этот датасет содержит информацию о репликах героев в формате source/target. Из аттрибутов: имена вершин и вес ребер. Графы будyт ненаправленными.
#в случае если есть проблема с нахождением шрифта
#font_import()
#loadfonts(device = "win")
<- read.csv("witcher_network.csv")
witcher <- witcher|>
witcher_1 rename(person_1 = Source, person_2 = Target)|>
select(-X, -Type)|>
filter(book == 1)
<- witcher|>
witcher_2 rename(person_1 = Source, person_2 = Target)|>
select(-X, -Type)|>
filter(book == 2)
Табличка для первой книги:
Табличка для второй книги:
Трансформируем датафреймы в графы. Добавляем аттрибут “degree” (отражает количество взаимодействий каждой вершины с другими)
<- graph_from_data_frame(witcher_1)
witcher_1_g <- graph_from_data_frame(witcher_2)
witcher_2_g
<- as.numeric(degree(witcher_1_g))
d_1 V(witcher_1_g)$degree <- d_1
<- as.numeric(degree(witcher_2_g))
d_2 V(witcher_2_g)$degree <- d_2
Далее приступаем к визуализации. Уже на этапе получения графа было видно, что в первой книге будет примерно в 1.5 раза больше вершин, поэтому раскладка для него подобрана другая.
Цветом обозначены имена нескольких героев, которые являются сюжетно важными для всех книг вселенной “Ведьмака”: самого Геральда, Цири, Лютика, Йеннифер и Трисс. Стояла задача узнать, насколько они важны в первых двух книгах.
#создаем доп. столбец, куда зановим "главность".
<- tibble(name = V(witcher_1_g)$name) |>
main_char_tbl 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
#загружаем кастомную палитру
<- paletteer_d("DresdenColor::colddays")
cols
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")
Уже было сказано, что первая книга представляет собой сборник рассказов. Теперь, когда у нас есть визуализация, важно отметить, что второстепенные персонажи из разных рассказов не связаны, и каждая глава образуем отдельную историю. Связующим звеном между ними является Геральт. На этом графе видно несколько кластеров – это и есть отдельные рассказы.
Обратим внимание на то, что в первой книге еще нет Цириллы(Цири). Все потому что на момент начала повествования она еще не родилась.Первая книга – предыстория ее семьи (и каким боком там появился Геральд).
Теперь визуализируем вторую книгу и посмотрим, что изменилось.
#создаем доп. столбец, куда зановим "главность".
<- tibble(name = V(witcher_2_g)$name) |>
main_char_tbl_2 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
#загружаем кастомную палитру
<- paletteer_d("DresdenColor::colddays")
cols
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 страниц, поэтому говорить о несоразмерности не приходится).