Сетевой анализ «Камер-фульерского журнала» В. Ф. Ходасевича

По данным за 1922 год

Сбор данных

library(ggraph)
library(paletteer)
library(igraph)
library(rdracor)
library(tidyverse)
library(rgexf)

Чтобы извлечь нужные намм данные из файлов GEXF, воспользуемся функцией.

all_files <- list.files(
  path = 'dataverse_files-3', 
  pattern = "\\.gexf$", 
  full.names = TRUE
)

process_network <- function(file_path) {
  
# Читаем GEXF
gexf_obj <- read.gexf(file_path)
  
# Конвертируем в igraph для анализа
ig_obj <- gexf.to.igraph(gexf_obj)
  
# Пример анализа: считаем количество узлов, ребер и плотность
stats <- list(
  name = basename(file_path),
  nodes_count = vcount(ig_obj),
  edges_count = ecount(ig_obj),
  density = edge_density(ig_obj)
  )
  
  return(list(graph = ig_obj, stats = stats))
}

results <- lapply(all_files, process_network)

summary_table <- do.call(rbind, lapply(results, function(x) as.data.frame(x$stats)))

# Достаем только объекты igraph из списка результатов
all_graphs <- lapply(results, function(x) x$graph)

# Объединяем все графы в один (это и есть обобщение за год)
# union объединяет узлы с одинаковыми именами
year_graph <- do.call(igraph::union, all_graphs)

# Проверим, что получилось
vcount(year_graph)
ecount(year_graph)

Итого уникальных персонажей за год — 112, уникальных связей — 707.

# Удалим изолированные узлы, то есть тех, кто ни с кем в этом году не взаимодействовал
year_graph <- delete.vertices(year_graph, V(year_graph)[degree(year_graph) == 0])

Создание объединенной сети

set.seed(424242)
ggraph(year_graph, layout = "nicely") +
  geom_edge_link(aes(alpha = ..index..), show.legend = FALSE, color = "grey") +
  geom_node_point(aes(size = degree(year_graph)), color = "#2c3e50") +
  geom_node_text(aes(label = name), repel = TRUE, size = 3, family = "sans") +
  theme_graph() +
  labs(
    title = "Объединенная сеть персонажей за год",
    subtitle = "На основе Камер-фурьерского журнала",
  )

Результат

В центре графа —плотный «комок» узлов с многочисленными связями. Это персоналии, которые чаще всего встречаются вместе и образуют устойчивую группу. По краям — разреженные углы; периферия — те, кто редко упоминается в журнале или связан лишь с парой других персонажей. Отдельные точки далеко от центра — фигуры, упомянутые в тексте, но практически не интегрированные в сеть взаимодействий.

Вычисляем лидеров

leaders <- data.frame(
  name = V(year_graph)$name,
  connections = degree(year_graph)
) |> 
  arrange(desc(connections)) |> 
  head(10)

Явный центр — Белый с 60 связями, он заметно отрывается от остальных. Далее идёт плотная группа почти на одном уровне: Вишняк, Шкловский и Бахрах (по 41), затем Эренбург (40) и Горький (39). Ниже — Каплун (37), Гржебин и Лурье_В (по 34), Оцуп (33).

Точки сочленения и клики

# Точки сочленения
articulation_points(year_graph)
# Клики
clique_num(year_graph)
cliques(year_graph, min=13)

Среди персонажей, удаление которых разрывает сеть на части —Зайцева_В, Женя, Слоним, Вишняк, Белый. Белый и Вишняк — фигуры ожидаемые. А вот Зайцева_В, Женя, Слоним —более интересные случаи. Их нет в топе по числу связей, но они все равно критичны.

Кликов четыре. Клики

Первая группа ориентирована на издательскую и литературную деятельность, это «деловой» центр сети. Гржебин — ключевой издатель того времени, через которого проходили почти все. Присутствие Роде, владельца знаменитого ресторана «Вилла Роде», намекает, что эти связи могли фиксироваться на совместных встречах или банкетах.

Вторая группа пересекается с первой через фигуру Горького. Здесь присутствуют люди, постоянно входившие в его домашний быт.

Третий и четвертый клики почти идентичны — это группа интеллектуалов: философ Бердяев соседствует с критиков Бахрахом и поэтом Оцупом. В 1922 году они, вероятно, находились в ситуации непрерывного общения.

K-ядра

Было решено выбрать ядра 11, 12 и 13, поскольку с ядрами меньшей степени граф оказывался слишком «запутанным».

set.seed(424242)
year_11 <- induced_subgraph(year_graph, vids=V(year_graph)[core > 10])
ggraph(year_11, layout = "stress") + 
  geom_edge_link(color = cols[3],
                 alpha = 0.3,
                 width = 0.6) +
  geom_node_point(aes(color = as.factor(core)),
                  size = 3, 
                  show.legend = TRUE) + 
  geom_node_text(aes(filter = wDegree > 10,
                     label = name),
                 color = cols[3],
                 repel = TRUE) +
  scale_color_brewer("k-ядра", type = "qual") +
  theme_void()

Граф с k-ядрами

Получилась сверхплотная группа. Ядра вложены друг в друга: сначала идут Осоргины и имеющие с ними связь персонажи, затем — более узкий круг (Роде, Пинкевич, Нина, Желябужский, Миклашевский), далее — крайне важные фигуры, в том числе центральные авторы (Белый, Оцуп, Пастернак, Бердяев).

Нахождение сообществ

Было решено использовать несколько методов нахождения сообществ.

# Случайное блуждание
cw <- cluster_walktrap(year_graph)
membership(cw) |> head()
par(mar = rep(0, 4))
plot(cw, year_graph)

# Cluster spinglass
csg <- cluster_spinglass(year_graph)
membership(csg) |> head()
par(mar = rep(0, 4))
plot(csg, year_graph)

# Louvain
louvain <- cluster_louvain(year_graph)
membership(louvain) |> head()
par(mar = rep(0, 4))
plot(louvain, year_graph)

Результаты применения алгоритма случайного блуждания таковы. Случайное блуждание

А вот что нам дало применение cluster spinglass. Cluster spinglass А это граф для алгоритма Лувена. Граф с k-ядрами

Чтобы понять, на результаты применения какого метода стоит обратить внимание, обратимся к значению модулярности для каждого из них. Таблица со значениями модулярности

Наиболее оправданным будет обращение к cluster spinglass. Модель нашла наиболее качественное разбиение: поскольку метод учитывает не только наличие ребер, но и их отсутствие, для сверхплотных сетей он может оказаться самом точным. Метод Лувена почти не отстает — значит, структура сообществ весьма устойчива. А вот «случайные блуждания» работают заметно хуже: алгоритм, «перепрыгивающий» между сообществами, сложнее понимает, где заканчивается одна группа и начинается другая.

Итак, в центре полученного путем применения cluster spinglass графа находятся «универсальные связные», принадлежащие сразу к нескольким контекстам (Белый, Эренбург, Бердяев, Одоевцева). Их сообщества накладываются друг на друга, образуя многослойное облако. Справа — Выгодский, Слоним, Григорович. Это более разреженная часть сети. Слева —Зернекау, Амфитеатров, Ларионов, Нончарова — также удаленная от центра группа. Возможно, эти люди принадлежали к кружкам, удаленным от «основного» узла, или находились географически далеко.