0

I am looking at how government agencies change over time. The plan is to use the ndtv package to visualise changes. I have an nodelist that lists vertex ids, agency name, node onset and node terminus:

nodelist <- read.csv("https://github.com/aterhorst/data/raw/master/nodelist.csv", header=T, stringsAsFactors = F)

and an edgelist showing onset, terminus of edges:

edgelist <- read.csv("https://github.com/aterhorst/data/raw/master/edgelist.csv", header=T, stringsAsFactors = F)

I can create a network object pretty easily:

nw <- network(edgelist,
              vertex.attr = nodelist[,c(1,2)],
              vertex.attrnames = c("vertex.id", "agency"), 
              directed = F)

nd <-networkDynamic(nw, 
                    edge.spells = edgelist[,c(3,4,2,1)],
                    vertex.spells=nodelist[,c(3,4,1)])

I can animate the network in terms of edges, vertices no problem:

reconcile.vertex.activity(nd, mode = "match.to.edges")

filmstrip(nd, 
          displaylabels = FALSE, 
          frames = 5, 
          slice.par = list(start = 2014, end = 2019, interval = 1, aggregate.dur = 1, rule = 'any'))

render.d3movie(nd,
               filename = "~/owncloud/longspine/data/animation.html",
               displaylabels = FALSE,
               # This slice function makes the labels work
               vertex.tooltip = function(slice) {paste("<b>Agency:</b>", (slice %v% "agency"))})

Essentially this shows how edges and vertices come and go over time. Next, I want to size vertices by agency budget. This changes from year to year. How should I do this? The online tutorials are a bit hard to intepret. In my example we have 217 agencies in total. Each would have an annual budget (if they exist as per onset, terminus in nodelist). Any tips or advice would be appreciated.

aterhorst
  • 625
  • 4
  • 14

2 Answers2

1

I managed to get something to work.

require(sna)
require(tsna)
require(ndtv)
require(networkDynamic)
require(lubridate)

nodelist <- read.csv("https://github.com/aterhorst/data/raw/master/nodelist.csv", header=T, stringsAsFactors = F)
edgelist <- read.csv("https://github.com/aterhorst/data/raw/master/edgelist.csv", header=T, stringsAsFactors = F)
nodelist_expanded <- read.csv("https://github.com/aterhorst/data/raw/master/nodelist_expanded.csv", header=T, stringsAsFactors = F)

# onset date must be numeric (does not like date?)
nodelist$onset <- year(nodelist$onset)
nodelist$terminus <- year(nodelist$terminus)

# colour nodes by portfolio type
nodelist$col <- ifelse(nodelist$portfolio == T, "red", "blue")

nodelist_expanded$onset <- year(nodelist_expanded$onset)
nodelist_expanded$terminus <- year(nodelist_expanded$terminus)

# scale attributes
nodelist_expanded$log_appropriation <- log(nodelist_expanded$appropriation + 10) / 10
nodelist_expanded$log_ext_revenue <- log(nodelist_expanded$ext_revenue + 10) / 10

edgelist$onset <- year(edgelist$onset)
edgelist$terminus <- year(edgelist$terminus)

# create basic network object
nw <- network(edgelist[,c(2,3)],
          vertex.attr = nodelist[,c(1:3,6)],
          vertex.attrnames = c("vertex.id", "agency", "portfolio", "col"),
          directed = F)

# plot basic network object
plot(nw, vertex.col = "col")

# make dynamic network object
nd <-networkDynamic(nw,
                    edge.spells = edgelist[,c(4,5,3,2)],
                    vertex.spells = nodelist_expanded[,c(6,7,1,8,9)],
                    create.TEAs = TRUE,
                    vertex.TEA.names = c("log_appropriation", "log_ext_revenue"))

# reconcile things
reconcile.vertex.activity(nd, mode = "match.to.edges")

# make movie!
render.d3movie(nd,
               displaylabels = FALSE,
               vertex.col = "col",
               vertex.tooltip = function(slice) {
             paste("<b>Agency:</b>", (slice %v% "agency"))})
aterhorst
  • 625
  • 4
  • 14
0

to set up dynamic vertex attributes you can use the activate.vertex.attribute() function to define which vertices should have what values for what duration. For example, to create a dynamic attribute on vertex 1 named 'budget' with value 10000 from 2014-2015:

    nd <-activate.vertex.attribute(nd,'budget',
                                  value=10000,
                                  onset=2014,
                                  terminus=2015,
                                  v=1)

Likely you would want to do this all at once when creating the object. If you set up your nodelist so that it has one row per vertex per year, you should be able to use the create.TEAs option of the networkDynamic() constructor to initialize the object with the attribute activity spells you need. So if your nodelist looks like:


      vertex.id        agency portfolio      onset   terminus  budget
    1         1   AAF Company     FALSE 2014-07-01 2015-07-01   10000
    2         1   AAF Company     FALSE 2015-07-01 2016-07-01   10500
    ...

then

    nd <-networkDynamic(nw, 
                    edge.spells = edgelist[,c(3,4,2,1)],
                    vertex.spells=nodelist[,c(3,4,1)],
                    create.TEAs=TRUE,
                    vertex.TEA.names='budget')

The section in the networkDynamic package vignette on "Activating TEA Attributes" should have more helpful info https://cran.r-project.org/web/packages/networkDynamic/vignettes/networkDynamic.pdf

You should then be able to map the dynamic vertex attribute to an animation plot property in ndtv (ndtv will manage the conversion from dynamic to static attribute at each time point as it renders)

render.d3movie(nd,vertex.cex='budget')

Unless these organizations have unusually modest budgets, I'm guessing you'd want to use the log() of the budget or some other way to transform the raw budget numbers or the nodes will be unimaginably big.

There is also a tutorial that covers this in more depth, as there is some necessary subtlety in how the aggregation of values in time windows work: http://statnet.csde.washington.edu/workshops/SUNBELT/current/ndtv/ndtv_workshop.html#controlling-plot-properties-using-dynamic-attributes-teas

skyebend
  • 1,079
  • 6
  • 19
  • BTW activity spells are "right-open" intervals, so I changed the terminus date to 07-01 to avoid leaving the organizations unfunded for one day – skyebend May 29 '19 at 04:55
  • Thanks. I am running into issues matching budgets with agencies so cannot test your solution yet. Linking budget papers with administrative arrangements is easier said than done. – aterhorst May 31 '19 at 06:23
  • nodelist_expanded <- read.csv("https://github.com/aterhorst/data/raw/master/nodelist_expanded.csv", header=T, stringsAsFactors = F) – aterhorst Jun 12 '19 at 06:47
  • nodelist_expanded needs dates to be aligned so there are no one day gaps. However, I do not understand TEAs too well. I get this error when trying to use nodelist_expanded: Error in networkDynamic(edge.spells = edgelist[, c(4, 5, 3, 2)], vertex.spells = nodelist_expanded[, : the vector of vertex.TEA.names must match the number of remaining columns in vertex.spells – aterhorst Jun 12 '19 at 06:50
  • Fixed dates so these align, now. – aterhorst Jun 12 '19 at 07:07