[Перевод] Визуализация статических и динамических сетей на R, часть 1
Очень многие системы и явления представимы в виде сетей, т.е. набора объектов и связей между ними. Сеть — не только абстракция, но и наглядный инструмент визуализации данных. Можно отобразить важность того или иного объекта, вес каждой связи, указать ключевые группы элементов, выделить их и подчеркнуть связи между ними. Главная задача визуализации — подать ключевую информацию о свойствах системы или явления максимально легким для восприятия способом. В идеальном случае анализ системы и визуализацию его результатов можно сделать в рамках одного инструмента. R с его обширным набором пакетов позволяет это.
Введение: визуализация сетей
Главное при проектировании визуализации сети — цель, которую нужно достичь. Какие структурные свойства мы хотели бы выделить?
Карты сетей — далеко не единственный инструмент визуализации графов — в некоторых случаях более предпочтительны другие форматы представления сетей, даже простые графики ключевых свойств.
В картах сетей, как и в других форматах визуализации, есть несколько ключевых настроек, влияющих на конечный результат. Главные — цвет, размер, форма и взаимное расположение.
Современные представления графов оптимизируют исходя из требований производительности и эстетических соображений. В частности, нужно минимизировать наложение и пересечение ребер, установить одинаковую длину ребер в графе.
Формат данных, размер и подготовка
В этом руководстве мы в основном будем работать с двумя небольшими массивами данных. Оба содержат информацию о средствах массовой информации. Один содержит сеть гиперссылок и упоминаний в новостных ресурсах. Другой — сеть ссылок между объектами и потребителями массовой информации. Хотя данных в примерах немного, многие идеи сгенерированных визуализаций можно распространить на средние и большие сети. По этой же причине мы редко будем использовать визуальные средства, например, форму символов-вершин: их практически невозможно различить в больших графах. Более того, при отображении очень больших сетей можно даже скрыть ребра, поскольку нужно сосредоточиться на выявлении и отображении групп вершин. Вообще говоря, размер сетей, которые можно визуализировать с помощью R, ограничен только объемом оперативной памяти вашей машины. Но следует подчеркнуть, что во многих случаях визуализация больших сетей в виде гигантского помпона куда менее полезна, чем графики с ключевыми свойствами графа.
В этом руководстве используются несколько ключевых пакетов, которые нужно установить, прежде чем продолжить. Будут упомянуты еще несколько библиотек, но они необязательны, и их можно пропустить. Будут использованы следующие основные библиотеки — igraph (поддерживается Габором Царди и Тамасом Непушем), sna, network (поддерживается Картером Баттсом и командой Statnet) и ndtv (поддерживается Скаем Бендер-деМолль).
install.packages("igraph")
install.packages("network")
install.packages("sna")
install.packages("ndtv")
Набор данных 1: список ребер
Первый набор данных, с которым предстоит работать, состоит из двух файлов: «Media-Example-NODES.csv» и «Media-Example-EDGES.csv» (скачать можно здесь).
nodes <- read.csv("Dataset1-Media-Example-NODES.csv", header=T, as.is=T)
links <- read.csv("Dataset1-Media-Example-EDGES.csv", header=T, as.is=T)
Исследуем данные:
head(nodes)
head(links)
nrow(nodes); length(unique(nodes$id))
nrow(links); nrow(unique(links[,c("from", "to")]))
Обратите внимание, что ребер больше, чем уникальных комбинаций «from»-«to». Это значит, что в данных есть случаи, когда между двумя вершинами более одной связи. Мы свернем все ребра одного типа между двумя узлами, просуммировав их веса с помощью функции aggregate()
по «from», «to» и «type»:
links <- aggregate(links[,3], links[,-3], sum)
links <- links[order(links$from, links$to),]
colnames(links)[4] <- "weight"
rownames(links) <- NULL
Набор данных 2: матрица
nodes2 <- read.csv("Dataset2-Media-User-Example-NODES.csv", header=T, as.is=T)
links2 <- read.csv("Dataset2-Media-User-Example-EDGES.csv", header=T, row.names=1)
Исследуем данные:
head(nodes2)
head(links2)
Можно убедиться, что links2 — матрица сопряжения для двусторонней сети:
links2 <- as.matrix(links2)
dim(links2)
dim(nodes2)
Визуализация сетей: первые шаги с igraph
Начнем с превращения исходных данных в сеть igraph. Для этого используем функцию igraph graph.data.frame, которая принимает на вход два блока данных: d и vertices.
- d описывает ребра сети. В первых двух колонках содержатся идентификаторы начальной и конечной вершины для каждого ребра. В следующих колонках находятся параметры ребра (вес, тип, метка, другое).
- vertices начинается с колонки идентификаторов вершин. Все следующие колонки интерпретируются как параметры вершины.
library(igraph)
net <- graph.data.frame(links, nodes, directed=T)
net
## IGRAPH DNW- 17 49 --
## + attr: name (v/c), media (v/c), media.type (v/n), type.label
## (v/c), audience.size (v/n), type (e/c), weight (e/n)
Описание объекта igraph начинается с четырех букв:
- D или U — для направленного или ненаправленного графа соответственно.
- N — для именованного графа (где у узлов есть атрибут
name
). - W — для взвешенного графа (где у связей есть атрибут
weight
). - B — для двустороннего графа (где у узлов есть атрибут
type
).
Следующие два числа (17 49) указывают количество вершин и ребер в графе. В описании также приводятся свойства вершин и ребер, например:
(g/c)
— свойство-строка на уровне графа(v/c)
— свойство-строка на уровне вершины(e/n)
— свойство-число на уровне ребра
Также легко получить доступ к вершинам, ребрам и их атрибутам:
E(net) # Ребра объекта "net"
V(net) # Вершины объекта "net"
E(net)$type # Свойство ребра "type"
V(net)$media # Свойство вершины "media"
# Можно работать с матрицей сети и напрямую:
net[1,]
net[5,7]
Теперь, когда есть сеть igraph, можно сделать первую попытку ее построить.
plot(net) # некрасивая картинка!
Получилось не слишком красиво. Давайте начнем улучшать картинку, убрав циклы в графе.
net <- simplify(net, remove.multiple = F, remove.loops = T)
Вы можете отметить, что можно было использовать simplify
, чтобы свернуть несколько ребер в одно, суммируя их веса с помощью команды типа simplify(net, edge.attr.comb=list(Weight="sum","ignore"))
. Проблема в том, что при совмещении не учитывается тип ребра (в наших данных «hyperlinks» — ссылки и «mentions» — упоминания).
Давайте также уменьшим размер стрелок и уберем метки (установив их в NA
):
plot(net, edge.arrow.size=.4,vertex.label=NA)
В части 2: шрифты и цвета в графиках R.