The netTS package is meant for relational data taking place through time. The point of this package is to facilitate the analyses of time-aggregated social networks using a moving window approach.
This approach allows a user to define the size of a time window (e.g., windowsize = 1 month) and the amount to move the window (e.g., windowshift = 1 day). This moving window then subsets the relational data within a window, creates a network and extracts a network measure. It then shifts over in time and repeats the process. By altering the size and shift of this moving window it is then possible to measure how networks change in time.
The main functions in the package are:
First install netTS and load some useful libraries
#if netTS is not yet installed you can use the following code to install it from github:
#devtools::install_github("tbonne/netTS")
library(netTS)
library(lubridate)
library(ggplot2)
library(igraph)
library(reshape2)
Here we make use of some vervet monkey grooming data (a highly gregarious primate). This data is in the rquired format for working with netTS. The first two coloumns should have information about what two individuals/entities are interacting. The third column should contain dates/times. This last column should be in date format (e.g., ymd, or ymd_hms). The lubridate package can be helpful for setting this up. Finaly, it is possible to record weights, sampling information, or any other useful information as additional columns.
#1. Take a look at the raw data
head(groomEvents)
## ID PartnerID date
## 1 Laur Malc 2015-07-01 12:32:19
## 2 Malc Laur 2015-07-01 12:33:01
## 3 Ubun Wall 2015-07-01 16:08:26
## 4 Wall Ubun 2015-07-01 16:09:52
## 5 Dott Jasm 2015-07-02 09:59:27
## 6 Jasm Dott 2015-07-02 10:01:58
The graphTS function requires windowsize and windowshift inputs to be in units of time. Again lubridate can be very useful here, as you can specify many kinds of times: e.g., years(10), months(10), hours(10), minutes(10), seconds(10). Additionally, a choice needs to be made for which network measure should be estimated through time. Here a measurement function needs to be created.
The idea of using functions is to provide flexibility and allow a wide range of network measures to be used. An example is provided below where: 1) a function is created, and 2) it is used to extract values from the data through time. The function below takes as an input a network and returns a single value - the mean out-degree of the network.
#1. create a small function
my.function.net <- function(graph){
#calculate some measure from the graph
my.value <- mean(degree(graph, mode="out"))
#return the value
return(my.value)
}
#2. extract values through time
graph.values <- graphTS(groomEvents, windowsize = days(60), windowshift = days(10), measureFun = my.function.net, directed=TRUE)
## [1] "45 networks extracted"
#3. Take a look at the outputs
head(graph.values)
## measure nEvents windowstart windowend
## 2 9.033333 503 2015-07-01 2015-08-30
## 3 9.066667 464 2015-07-11 2015-09-09
## 4 10.400000 542 2015-07-21 2015-09-19
## 5 11.620690 612 2015-07-31 2015-09-29
## 6 11.517241 582 2015-08-10 2015-10-09
## 7 10.758621 523 2015-08-20 2015-10-19
Plot the graph level measures through time
#1. Plot the results
graphTS.plot(graph.values)
It is also possible to ask how the network changes through time at the node level (i.e., the ego network). Here we look at how nodes change in out-degree through time. The measureFun for node level analysis should be a function that takes one network and returns a value for each node. An example is provided below.
#1. create a small function
my.function.nodes <- function(graph){
#calculate some measure from the graph
my.value <- degree(graph, mode="out")
#return the values
return(my.value)
}
#2. extract values through time
node.values <- nodeTS(groomEvents, windowsize = days(60), windowshift = days(10), measureFun = my.function.nodes, directed=TRUE)
## [1] "45 networks extracted"
#3. Take a look at the output
head(node.values)
## Laur Malc Ubun Wall Dott Jasm Meg Gunt Tear Zool Glit Xavi Razo Kels
## 2 13 12 10 8 13 11 11 8 9 8 2 7 11 12
## 3 8 12 13 8 13 11 11 6 8 8 1 8 10 12
## 4 10 14 13 11 14 11 12 6 8 11 1 9 14 14
## 5 13 14 15 12 16 11 14 6 10 14 NA 8 15 16
## 6 14 13 15 11 16 11 14 6 10 14 NA 9 15 16
## 7 12 10 14 11 15 12 14 7 11 13 NA 9 13 15
## Shum Cycl Pris Arwe Obi Dodg Nina Bone Pene Dant Schm Ella Alla Valk
## 2 13 9 8 10 14 8 10 5 16 7 9 6 6 5
## 3 13 11 9 11 15 8 9 6 17 6 10 3 5 6
## 4 13 13 11 11 16 8 9 8 21 7 11 4 10 8
## 5 13 14 12 13 16 8 8 9 20 7 12 5 11 11
## 6 11 14 12 12 16 9 8 9 20 7 12 5 11 10
## 7 8 14 12 12 13 6 7 8 19 5 10 7 10 11
## Kins Laya Keit Yoda Clou Tren nEvents windowstart
## 2 3 7 NA NA NA NA 503 2015-07-01 12:32:19
## 3 5 9 NA NA NA NA 464 2015-07-11 12:32:19
## 4 5 9 NA NA NA NA 542 2015-07-21 12:32:19
## 5 5 9 NA NA NA NA 612 2015-07-31 12:32:19
## 6 5 9 NA NA NA NA 582 2015-08-10 12:32:19
## 7 6 8 NA NA NA NA 523 2015-08-20 12:32:19
## windowend
## 2 2015-08-30 12:32:19
## 3 2015-09-09 12:32:19
## 4 2015-09-19 12:32:19
## 5 2015-09-29 12:32:19
## 6 2015-10-09 12:32:19
## 7 2015-10-19 12:32:19
Plot node level changes
nodeTS.plot(node.values)
It is also possible to look at how relationships between two individuals change in time. This is the dyadic level. Here we look at the strength of each dyad trough time, i.e. their number of interactions. A measureFun for dyadic measures should be a function that takes one network and returns a value for each dyad. Names should be associated with each value to ensure that values are associated with the correct dyad. An example is provided below.
#1. create a small function
my.function.dyads <- function(graph){
#calculate some measure from the graph
my.value <- E(graph)$weight
#assign each weight the correct dyad name
names(my.value) <- paste(get.edgelist(graph)[, 1], get.edgelist(graph)[,
2], sep = "_")
#return the values
return(((my.value)))
}
#2. extract values through time
dyad.values <- dyadTS(groomEvents, windowsize = days(60), windowshift = days(10), measureFun = my.function.dyads, directed=TRUE)
## [1] "45 networks extracted"
#3. take a look at the output
dyad.values[1:10,1:5]
## Laur_Malc Malc_Laur Ubun_Wall Wall_Ubun Dott_Jasm
## 2 1 2 1 1 7
## 3 NA 1 2 2 4
## 4 NA 1 2 2 3
## 5 1 2 2 2 3
## 6 1 2 2 2 3
## 7 1 1 2 3 2
## 8 1 1 2 3 4
## 9 1 1 NA 1 4
## 10 1 1 NA 1 4
## 11 NA NA NA 1 4
Plot dyad level changes
dyadTS.plot(dyad.values)