Ходасевич

Автор

Влада Берлин

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

20 марта 2025 г.

Подготовка данных

Установка библиотек

library(igraph)
library(rgexf)
library(tidyverse)
library(visNetwork)

Подготовка данных

В данной работе используется датасет на основе «Камер-фурьерский журнала» Ходасевича. Мы будем использовать данные за 1932 год, потому что согласно данным из (Орехов Б. В. 2018), там содержится самое большое количество людей.

setwd('.\\months_data')
list_files <- list.files(pattern = '1932_.*\\.gexf', full.names = TRUE)
df <- tibble(from_name = character(), to_name = character(), weight = character())

for (f in list_files) {
  d <- read.gexf(f) |>
    gexf.to.igraph() |>
    as_long_data_frame() |>
    select(from_name, to_name, weight)
  df <- merge(d, df, all=TRUE)  # Объединяем в один датафрейм
  df <- df |>   # Суммируем повторяющиеся векторы
    group_by(from_name, to_name) |>
    summarize(weight = sum(weight))
}
df

Создаем граф

graph <- graph_from_data_frame(df)
graph
IGRAPH 383877b DNW- 207 1909 -- 
+ attr: name (v/c), weight (e/n)
+ edges from 383877b (vertex names):
 [1] Абрамова  ->Зайцев_Б       Абрамова  ->Зайцева_В     
 [3] Авксентьев->Алданов        Авксентьев->Вишняк        
 [5] Авксентьев->Вольфсон       Авксентьев->Вольфсон_Э    
 [7] Авксентьев->Демидов        Авксентьев->Керенский     
 [9] Авксентьев->Коварский      Авксентьев->Маклаков      
[11] Авксентьев->Маргулиес      Авксентьев->Маргулиес_Марг
[13] Авксентьев->Пети           Авксентьев->Руднев        
[15] Аврех     ->Азов           Аврех     ->Благов        
+ ... omitted several edges

У нас получился направленный, именованный и взвешенный граф с 207 вершинами и 1909 связями.

Плотность

edge_density(graph)
[1] 0.04476807

Компоненты

components(graph)$csize
[1] 195   2   5   2   3

В этом графе 4 компоненты, в главной компоненте 195 узлов. Посмотрим, кто не попал в главную компоненту

which(components(graph)$membership !=1)
        Аитов   Горчаков_МК      Именитов    Кугульский     Ник_Алеев 
            7            52            75            93           125 
      Татищев жена_Шильтяна   Алексинский     Именитова       Шильтян 
          161           173           180           198           199 
      Любимов     Шишмарева 
          204           206 

Самое длинное расстояние между узлами (оно же диаметр)

lgc <- largest_component(graph)
farthest_vertices(lgc)
$vertices
+ 2/195 vertices, named, from 3887688:
[1] Полонская Полонский

$distance
[1] 22

Интересное наблюдение, так как из-за совпадения фамилий это скорее всего супруги.

Транзитивность

transitivity(graph)
[1] 0.4954983

Взвешенная центральность

Рассчитаем взвешенную центральность

wDegree <- strength(graph)
sort(wDegree, decreasing = TRUE)[1:5]
    Алданов        Нина  Смоленский Мандельштам         Оля 
        223         207         201         192         171 

Визуализация графа

vis_g <- toVisNetworkData(graph)
g_3d <- visNetwork(nodes = vis_g$nodes, 
                             edges = vis_g$edges,
                             width = "100%", 
                             height = 600)
visOptions(g_3d, 
           highlightNearest = list(enabled = TRUE, degree = 1, hover = TRUE), 
           nodesIdSelection = FALSE)  |> 
  visPhysics(maxVelocity = 20, stabilization = TRUE)  |>  
  visInteraction(dragNodes = TRUE)

Подграф

Сделаем подграф из узлов, у которых самая большая взыешенная центральность. Мы будем использовать метод к-ядер, так как к-ядро является “популярным определением социальной сплоченности”.

V(graph)$degree <- wDegree
degr_graph <- which(V(graph)$degree >= 100)
subgr <- induced_subgraph(graph, vids=degr_graph)
vis_sg <- toVisNetworkData(subgr)
vis_sub <- visNetwork(nodes = vis_sg$nodes, 
                             edges = vis_sg$edges,
                             width = "100%", 
                             height = 600)
visOptions(vis_sub, 
           highlightNearest = list(enabled = TRUE, degree = 1, hover = TRUE), 
           nodesIdSelection = FALSE)  |> 
  visPhysics(maxVelocity = 20, stabilization = TRUE)  |>  
  visInteraction(dragNodes = TRUE)

Модулярность и анализ сообществ

Если все узлы принадлежат к одному классу, то модулярность равна нулю. Если разбиение на классы хорошее, то модулярность должна быть высокая. Сравним два алгоритма, чтобы понять, у какого модулярность больше. ### Алгоритм “случайного блуждания”

modularity(cluster_walktrap(graph))
[1] 0.4182526

Главный собственный вектор

modularity(cluster_leading_eigen(graph))
[1] 0.3895832

Алгоритм “случайного блуждания” более высокий, значит, лучше использовать его для разбиения на сообщества.

plot(cluster_walktrap(graph), graph)

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

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

articulation_points(graph)
+ 9/207 vertices, named, from 383877b:
[1] Каплун       Крыжановская Костанов     Женя         Нина        
[6] Муратов      Оля          Вишняк       Именитов    

Клики

clique_num(graph)
[1] 25

Размер наибольшей клики равен 25, выведем ее узлы.

largest_cliques(graph)
[[1]]
+ 25/207 vertices, named, from 383877b:
 [1] Зайцев_Б      Аминадо       Шполянская    Цетлина       Цетлин       
 [6] Фондаминский  Тумаркин      Трахтерев     Смоленский    Руманов      
[11] Ремизов       Осоргина      Нина          Милочка       Маршак       
[16] Макеев        Куприн        Зайцева_В     Жаботинский   Гальперин    
[21] Вольфсон_Э    Вольфсон      Вейдле        Болотова_Шура Ася          

использованная литература

Орехов Б. В., Файнберг В. В., Успенский П. Ф. 2018. «Цифровые подходы к «Камер-фурьерскому журналу» В. Ф. Ходасевича».