[Перевод] Визуализация статических и динамических сетей на 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)


5fca552eafb245cf9f2a1da4780d3007.png

Простая анимация графиков в 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

0d47095128824e0fbc15f167997c670c.gif

Интерактивные сети с 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') 


4ffdc4daa2a94d9eb744512490c32674.pngАнимация эволюции сети
Анимированные визуализации — хороший способ показать эволюцию во времени небольшой или среднего размера сети. На данный момент, 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) 


cf7114c9654b4d28867570ac32e71789.png

Построим сеть в момент времени 1:

plot( network.extract(short.stergm.sim, at=1) )


57116dfcd4c44286bfbcab1c34765d5d.png

Построим вершины и ребра, активные с первого по пятый момент времени:

plot( network.extract(short.stergm.sim, onset=1, terminus=5, rule="all") )


4bdb65bf3b9c421691d69bb63fd7d1f5.png

Построим вершины и ребра, активные с первого по десятый момент времени:

plot( network.extract(short.stergm.sim, onset=1, terminus=10, rule="any") ) 


8db156317cb7455d9bf34f1c35d36ab5.png

Давайте сделаем короткую трехмерную анимацию сети из примера:

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'))

6724e9894b424970b729d44c9972d964.png

Можно рассчитать заранее координаты анимации (в противном случае они считаются в момент создания анимации). Здесь 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' )


9ea0062bba454e2f9c93f05f4df6c3d4.png

В дополнение к динамическим вершинам и ребрам 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')


25bdf271007d4005ab43c30d8c598f54.png

© Habrahabr.ru