[Перевод] Визуализация статических и динамических сетей на R, часть 7, последняя
В первой части:
- визуализация сетей: зачем? каким образом?
- параметры визуализации
- best practices — эстетика и производительность
- форматы данных и подготовка
- описание наборов данных, которые используются в примерах
- начало работы с igraph
Во второй части: цвета и шрифты в графиках R.
В третьей части: параметры графов, вершин и ребер.
В четвертой части: размещения сети.
В пятой части: акцентирование свойств сети, вершин, ребер, путей.
В шестой части: интерактивная визуализация сетей, другие способы представления сети.
В этой части: анимированная визуализация сетей, эволюция сети во времени.
Интерактивная и анимированная визуализация сети
Интерактивные трехмерные сети в R
Сейчас все легче экспортировать графики R в html/javascript. Есть много библиотек, таких как rcharts
и htmlwidgets
, которые пригодятся при создании веб-графиков непосредственно из R. Мы посмотрим на библиотеку networkD3, которая, как видно из названия, создает интерактивные визуализации сетей с помощью javascript библиотеки D3.
Следует понимать, что визуализации, созданные с помощью networkD3
, наиболее полезны как отправная точка для дальнейшей работы. Если вы немного знаете javascript, можно их использовать как первый шаг и изменять до получения желаемого результата.
Если у вас нет библиотеки networkD3
, установите ее:
install.packages("networkD3")
Данные, которая использует эта библиотека — стандартный список ребер с парой небольших изменений. Для того, чтобы все заработало, нужно, чтобы идентификаторы вершин были числовыми и начинались с нуля. Легкий способ добиться этого — превратить наши буквенные идентификаторы в факторную переменную, полученный результат — в число, и обеспечить нумерацию с нуля вычитанием единицы.
library(networkD3)
el <- data.frame(from=as.numeric(factor(links$from))-1,
to=as.numeric(factor(links$to))-1 )
Вершины должны быть в том же порядке, что и колонка-«источник» в связях:
nl <- cbind(idn=factor(nodes$media, levels=nodes$media), nodes)
Теперь можно создать интерактивное представление. Параметр Group
используется для раскрашивания вершин. Nodesize
— это не размер вершины, как можно было бы подумать, а номер колонки в данных о вершинах, которую нужно использовать для задания размера. Параметр charge
управляет отталкиванием (меньше нуля) или притяжением (больше нуля).
forceNetwork(Links = el, Nodes = nl, Source="from", Target="to",
NodeID = "idn", Group = "type.label",linkWidth = 1,
linkColour = "#afafaf", fontSize=12, zoom=T, legend=T,
Nodesize=6, opacity = 0.8, charge=-300,
width = 600, height = 400)
Простая анимация графиков в R
Если вы уже устанавливали ndtv, у вас также должен стоять пакет animation. Если же нет, сейчас самое время его установить — install.packages('animation')
. Обратите внимание, этот пакет позволяет без труда создавать разные (не обязательно относящиеся к сетям) анимации в R. Это работает так: создается несколько графиков, которые потом объединяются в GIF.
Подвох здесь следующий: чтобы это заработало, нужен не только пакет R, но и специальная программа ImageMagick.
library(animation)
library(igraph)
ani.options("convert") # Убедитесь, что пакет знает, где находится ImageMagick
# Если не находит, укажите правильный путь в вашей системе.
ani.options(convert="C:/Program Files/ImageMagick-6.8.8-Q16/convert.exe")
Теперь мы создадим 4 графика сети (те же, что и раньше), только в этот раз внутри команды saveGIF
. Интервал анимации устанавливается параметром interval
, параметр movie.name
устанавливает название gif-файла.
l <- layout.fruchterman.reingold(net)
saveGIF( { col <- rep("grey40", vcount(net))
plot(net, vertex.color=col, layout=l)
step.1 <- V(net)[media=="Wall Street Journal"]
col[step.1] <- "#ff5100"
plot(net, vertex.color=col, layout=l)
step.2 <- unlist(neighborhood(net, 1, step.1, mode="out"))
col[setdiff(step.2, step.1)] <- "#ff9d00"
plot(net, vertex.color=col, layout=l)
step.3 <- unlist(neighborhood(net, 2, step.1, mode="out"))
col[setdiff(step.3, step.2)] <- "#FFDD1F"
plot(net, vertex.color=col, layout=l) },
interval = .8, movie.name="network_animation.gif" )
detach(package:igraph)
detach(package:animation
Интерактивные сети с ndtv-d3
Интерактивные графики статических сетей
В этом разделе мы создадим более сложные визуализации с пакетом ndtv
. Для создания анимаций не нужно дополнительное ПО. Если же вы хотите сохранять анимации как видео-файлы (посмотрите ?saveVideo
), вам понадобится видео-конвертер FFmpeg. Чтобы узнать, какой инсталлятор подходит для вашей ОС, выполните ?install.ffmpeg
. Для того, чтобы использовать все доступные расположения, вам также понадобится Java.
install.packages("ndtv", dependencies=T)
Поскольку этот пакет является часть Statnet, он будет принимать объекты из пакета network
, как тот, что мы создавали раньше.
library(ndtv)
net3
Большинство параметров имеют интуитивно понятные названия (bg
— цвет фона графика). Два новых параметра, которые раньше не использовались — vertex.tooltip
и edge.tooltip
. Они содержат информацию, которую можно увидеть, наведя курсор мыши на элемент сети. Обратите внимание, параметры для всплывающих подсказок принимают теги html — например, используем тег разрыва строки — <br>
. Параметр launchBrowser
указывает R открыть получившийся файл с визуализацией (filename
) в браузере.
render.d3movie(net3, usearrows = F, displaylabels = F, bg="#111111",
vertex.border="#ffffff", vertex.col = net3 %v% "col",
vertex.cex = (net3 %v% "audience.size")/8,
edge.lwd = (net3 %e% "weight")/3, edge.col = '#55555599',
vertex.tooltip = paste("<b>Name:</b>", (net3 %v% 'media') , "<br>",
"<b>Type:</b>", (net3 %v% 'type.label')),
edge.tooltip = paste("<b>Edge type:</b>", (net3 %e% 'type'), "<br>",
"<b>Edge weight:</b>", (net3 %e% "weight" ) ),
launchBrowser=F, filename="Media-Network.html", output.mode='inline')
Анимация эволюции сети
Анимированные визуализации — хороший способ показать эволюцию во времени небольшой или среднего размера сети. На данный момент, ndtv
— лучший пакет R, предназначенный для этой цели — особенно после того, как недавно была добавлена возможность трехмерной визуализации.
Для того, чтобы работать с анимацией сети в ndtv
, нужно понимать динамический формат сети Statnet, реализованный в пакете networkDynamic
. Давайте посмотрим на один из наборов данных, включенный в пакет как пример:
data(short.stergm.sim)
short.stergm.sim
head(as.data.frame(short.stergm.sim))
## onset terminus tail head onset.censored
## 1 0 1 3 5 FALSE
## 2 10 20 3 5 FALSE
## 3 0 25 3 6 FALSE
## 4 0 1 3 9 FALSE
## 5 2 25 3 9 FALSE
## 6 0 4 3 11 FALSE
## terminus.censored duration edge.id
## 1 FALSE 1 1
## 2 FALSE 10 1
## 3 FALSE 25 2
## 4 FALSE 1 3
## 5 FALSE 23 3
## 6 FALSE 4 4
Здесь мы видим список ребер. Каждое ребро исходит из вершины, идентификатор которой находится в колонке tail
, и входит в вершину с идентификатором в колонке head
. Ребро существует от временной отметки onset
до временной отметки terminus
. Onset и terminus, помеченные censored, означают начало и конец наблюдения за сетью, а не фактическое образование и исчезновение связей.
Можно легко построить сеть, не принимая во внимание ее временную составляющую (объединив все когда-либо представленные вершины и ребра):
plot(short.stergm.sim)
Построим сеть в момент времени 1:
plot( network.extract(short.stergm.sim, at=1) )
Построим вершины и ребра, активные с первого по пятый момент времени:
plot( network.extract(short.stergm.sim, onset=1, terminus=5, rule="all") )
Построим вершины и ребра, активные с первого по десятый момент времени:
plot( network.extract(short.stergm.sim, onset=1, terminus=10, rule="any") )
Давайте сделаем короткую трехмерную анимацию сети из примера:
render.d3movie(short.stergm.sim,displaylabels=TRUE)
Теперь мы готовы создавать и анимировать свою собственную динамическую сеть. Объект-динамическую сеть можно получить несколькими способами: из набора сетей / матриц, представляющих разные моменты времени, из матриц / наборов данных со списками вершин и ребер, где указано, когда они активны или когда меняют состояние. Больше информации доступно в ?networkDynamic
.
Добавим временной компонент в наш пример со средствами массовой информации. Код ниже берет временной интервал от 0 до 50 и делает активными вершины сети все это время. Ребра появляются одно за другим, каждое активно с момента появления до 50-го момента времени. Создадим такую сеть с помощью networkDynamic
, время для вершин — node.spelss
, для ребер — edge.spells
.
vs <- data.frame(onset=0, terminus=50, vertex.id=1:17)
es <- data.frame(onset=1:49, terminus=50,
head=as.matrix(net3, matrix.type="edgelist")[,1],
tail=as.matrix(net3, matrix.type="edgelist")[,2])
net3.dyn <- networkDynamic(base.net=net3, edge.spells=es, vertex.spells=vs)
Если просто пытаться построить networkDynamic-сеть, получится комбинированная сеть за весь период наблюдения, т.е. исходный пример со средствами массовой информации.
plot(net3.dyn, vertex.cex=(net3 %v% "audience.size")/7, vertex.col="col")
Один из способов показать эволюцию сети во времени — статические картинки для разных моментов времени. Хотя можно генерировать их по одной, как мы делали выше, ndtv
предлагает более легкий способ. Команда для этого — filmstrip
. Как и в функции par()
, управляющей параметрами графиков R, здесь mfrow
устанавливает количество строк и столбцов в таблице на несколько графиков.
filmstrip(net3.dyn, displaylabels=F, mfrow=c(1, 5),
slice.par=list(start=0, end=49, interval=10,
aggregate.dur=10, rule='any'))
Можно рассчитать заранее координаты анимации (в противном случае они считаются в момент создания анимации). Здесь animation.mode
— алгоритм расположения — один из «kamadakawai», «MDSJ», «Graphviz» и «useAttribute» (координаты, заданные пользователем).
compute.animation(net3.dyn, animation.mode = "kamadakawai",
slice.par=list(start=0, end=50, interval=1,
aggregate.dur=1, rule='any'))
render.d3movie(net3.dyn, usearrows = F,
displaylabels = F, label=net3 %v% "media",
bg="#ffffff", vertex.border="#333333",
vertex.cex = degree(net3)/2,
vertex.col = net3.dyn %v% "col",
edge.lwd = (net3.dyn %e% "weight")/3,
edge.col = '#55555599',
vertex.tooltip = paste("<b>Name:</b>", (net3.dyn %v% "media") , "<br>",
"<b>Type:</b>", (net3.dyn %v% "type.label")),
edge.tooltip = paste("<b>Edge type:</b>", (net3.dyn %e% "type"), "<br>",
"<b>Edge weight:</b>", (net3.dyn %e% "weight" ) ),
launchBrowser=T, filename="Media-Network-Dynamic.html",
render.par=list(tween.frames = 30, show.time = F),
plot.par=list(mar=c(0,0,0,0)), output.mode='inline' )
В дополнение к динамическим вершинам и ребрам ndtv принимает динамические параметры. Можно было их добавить в наборы данных es и vs выше. Более того, функция построения может применять специальные параметры и создавать динамические аргументы непосредственно в процессе. Например, function(slice) { какие-то вычисления со slice } произведет действия в текущем временном срезе, позволяя тем самым динамически изменять параметры. Обратите внимание на размер вершин ниже:
compute.animation(net3.dyn, animation.mode = "kamadakawai",
slice.par=list(start=0, end=50, interval=4,
aggregate.dur=1, rule='any'))
render.d3movie(net3.dyn, usearrows = F,
displaylabels = F, label=net3 %v% "media",
bg="#000000", vertex.border="#dddddd",
vertex.cex = function(slice){ degree(slice)/2.5 },
vertex.col = net3.dyn %v% "col",
edge.lwd = (net3.dyn %e% "weight")/3,
edge.col = '#55555599',
vertex.tooltip = paste("<b>Name:</b>", (net3.dyn %v% "media") , "<br>",
"<b>Type:</b>", (net3.dyn %v% "type.label")),
edge.tooltip = paste("<b>Edge type:</b>", (net3.dyn %e% "type"), "<br>",
"<b>Edge weight:</b>", (net3.dyn %e% "weight" ) ),
launchBrowser=T, filename="Media-Network-even-more-Dynamic.html",
render.par=list(tween.frames = 15, show.time = F), output.mode='inline')