Making Network Animations Using NDTV package

The following commands are for making a network animation using statnet and ndtv in R.

Step 1: Load packages

I’ll use statnet, ndtv, and rio packages to make a network gif. statnet and ndtv packages are necessary but you can use other packages instead of rio for loading data. I prefer rio because the command is simple. What we need to do is type ‘import’ as shown in Step 2.

library(statnet)
library(ndtv)
library(rio)

Step 2: Load data

I’ll make a network animation for the formation and dissolution of interstate rivalry from 1987 to 2002 with 5-year intervals (four time points). So, two pieces of information are necessary: 1) a list of rivalries for the given time points (edge list), and 2) a list of countries (node list). The current version of ndtv package doesn’t allow varying numbers of vertices, which means the number of vertices must be the same for all time points. Indeed, some countries newly appeared while others disappeared during the given time period. However, because of the limitation in ndtv, I put all countries existed for the given period in my node list (node.csv).

Additionally, I’ll color the nodes (countries) using their geographical location. For the color of the nodes, I’ll use COW country codes, which are included in ‘vtx1987.csv’ below.

You can download these toy datasets from my github.

riv <- import("C:/Users/bomim/Documents/website-hugo/content/post/toyriv.dta")
vtx <- import("C:/Users/bomim/Documents/website-hugo//content/post/node.csv")
vtx87 <- import("C:/Users/bomim/Documents/website-hugo/content/post/vtx1987.csv")

Step 3: Look at the data

Let’s look at the data we loaded in the previous step. The object ‘riv’ is a list of rivalries (edge list). As shown in the table below, ‘riv’ has four columns: year, stateabb1, stateabb2, and no. Since rivalries are not directed (if Country A is Country B’s rival, then Country B is Country A’s rival as well), the order of stateabb1 and stateabb2 is not important. I’ll use ‘no’ variable to differentiate the four time points from one another.

Because our edge list is made using stateabb (i.e., AAB, AFG, ALB…), our node list must be a list of stateabb.

In terms of the color of nodes, I need more info about the nodes (countries). So, I use the variables in ‘vtx87’. The nodes will be matched using the ‘stateabb’ variable.

head(riv)
##   year stateabb1 stateabb2 no
## 1 1987       AFG       PAK  1
## 2 1992       AFG       PAK  2
## 3 1997       AFG       PAK  3
## 4 2002       AFG       PAK  4
## 5 1997       AFG       TAJ  3
## 6 1997       AFG       UZB  3
head(vtx)
##   stateabb
## 1      AAB
## 2      AFG
## 3      ALB
## 4      ALG
## 5      AND
## 6      ANG
head(vtx87)
##   stateabb ccode        cinc
## 1      AAB    58 0.000002740
## 2      AFG   700 0.001255717
## 3      ALB   339 0.000529850
## 4      ALG   615 0.004066098
## 5      AND   232 0.000000000
## 6      ANG   540 0.001150265

Step 4: Make a list of networks

I use ‘for loop’ to make a list of rivalry networks for four time points. Briefly, I make an empty list (rivlist) first and put each rivalry network for each time point in the list using the for loop.

  • More details

The object, ‘nv’, is a numeric value, which shows the total number of countries (the value is 204 in my toy dataset).

Let’s look at the for loop chunk below. This loop will be repeated four times (from 1 to 4). I use ‘no’ for the time points. When i is equal to 1, all rivalries in 1987 are selected (rivi) and two columns from rivi (stateabb1 and stateabb2) would be kept (rivii).

Using the nv (204L), I make an empty network (command: network.initialize). Since there is no direction in rivalries, the option ‘directed’ is FALSE. If your networks of interest are directed (i.e., A thinks B is his/her friend but B doesn’t think like that), then change this option to TRUE. For the network with 204 vertices, I add the vertex names using the command, ‘network.vertex.names’. Now the vertices in the network have their names (stateabb) but still there is no information about ties. The information of ties is loaded from ‘rivii’. The next line is optional. I add info about time points as a vertex attribute (order) to the network. As mentioned above, this sequence is repeated for four time points.

nv <- nrow(vtx)

rivlist <- list()

for(i in 1:4){
  rivi <- subset(riv, riv$no == i)
  rivii<-cbind(rivi$stateabb1, rivi$stateabb2)
  netyr <- network.initialize(nv, directed=FALSE)
  network.vertex.names(x=netyr) <- vtx$stateabb
  netyr[rivii] <- 1
  set.vertex.attribute(x=netyr,attrname="order",val=rep(i,nv))
  rivlist[[i]] <- netyr
}
  • Set vertex attribute

As shown in the previous step, we can use ‘set.vertex.attribute’ to add vertex attributes to the network. Following commands are for the color of nodes.

From ‘vtx87’, I add COW country codes for the nodes in the network. This variable is for the color of nodes: red for Americas, orange for Europe, yellow for Africa, green for Middle East, and blue for Asia and Oceania. If you want to color the nodes, you’d better to choose one of the variables which rarely varies over time such as geographical location for countries and gender for individuals. Although ndtv allows us to look at changes in networks such as tie formation and dissolution, it doesn’t allow changes in nodal attributes. For instance, if Country A moved to democracy from non-democracy in 1992 (time point 2), its change cannot be reflected in the network animation.

set.vertex.attribute(rivlist[[1]],names(vtx87),vtx87)

cols1<-vector()
for(i in 1:204){
  ifelse(rivlist[[1]]$val[[i]]$ccode<200, cols1[i] <- "red", 
         ifelse((rivlist[[1]]$val[[i]]$ccode>=200)&(rivlist[[1]]$val[[i]]$ccode<400), cols1[i]<-"orange",
                ifelse((rivlist[[1]]$val[[i]]$ccode>=400)&(rivlist[[1]]$val[[i]]$ccode<630), cols1[i]<-"yellow",
                       ifelse((rivlist[[1]]$val[[i]]$ccode>=630)&(rivlist[[1]]$val[[i]]$ccode<700),cols1[i]<-"green", cols1[i]<-"blue"))))
}

** You can find more commands for data management in R here from my github.

  • Make a plot

Let’s look at the rivalry network in 1987 (time point 1). I use the vector of colors (for geographical location) for this plot.

plot(rivlist[[1]], 
     vertex.col=cols1, 
     edge.col="grey50",
     xpd=T)

Step 5: Make GIFs!

This is the last step! Make a networkDynamic object using the command ‘networkDynamic’. Before we render an animation, we need one more code here. By using the ‘compute.animation’ command, we can set intervals, animation layout, etc.

rivdyn <- networkDynamic(network.list = rivlist)
rivgif <- compute.animation(rivdyn, 
                            animation.mode = "kamadakawai",
                            verbose = FALSE)

This is the last command. Use the ‘render.d3movie’ command to make a network animation! I add the color vector for this gif!

render.d3movie(rivgif,
               displaylables=FALSE, 
               vertex.col=cols1)

More information about ndtv can be found here.

You can look at the network gif by clicking Video button above.

Bomi K. Lee
Bomi K. Lee
Postdoctoral Research Fellow

My research interests include international conflict, rivalry, and political methodology.