This vignette uses the Knecht
data from Knecht (2006) and Knecht (2008) to provide an illustrative example of estimating an ego-Temporal Exponential Random Graph Model (ego-TERGM) using the egoTERGM
package (A. Knecht 2006, A. B. Knecht (2008)). It begins by estimating an ego-TERGM on the Knecht friendship network, a network of 26 students in a Dutch classroom measured across four time-periods. In addition to data on the friendships between students there is nodal attribute data on a variety of demographic and behavioral factors. For a detailed explanation of the data, I refer the reader to the original publications on the data or to the xergm.common
help page for the knecht
data: ??knecht
.
Much of the code for estimating the ego-TERGM is a modification of code originally produced for its cross-sectional equivalent (Salter-Townshend and Brendan Murphy 2015). This vignette will proceed step-by-step in presenting the typical workflow for estimating and interpreting an ego-TERGM using egoTERGM
.
Note: The code presented here is not built given the runtime for the ego-TERGM presented here. As such, the code presented is purely illustrative. It will successfully run, however.
The following chunk of code imports the Knecht friendship network gathered by Andrea Knecht and provided in the xergm.common
package (Leifeld, Cranmer, and Desmarais 2014, leifeld2017temporal). It also uses some code from the xergm.common
package to prepare the network for the ego_tergm
function, note that it produces a list of network objects.
library(xergm.common)
set.seed(1)
data("knecht")
for (i in 1:length(friendship)) {
rownames(friendship[[i]]) <- paste("Student.", 1:nrow(friendship[[i]]), sep="")
colnames(friendship[[i]]) <- paste("Student.", 1:nrow(friendship[[i]]), sep="")
}
rownames(primary) <- rownames(friendship[[1]])
colnames(primary) <- colnames(friendship[[1]])
sex <- demographics$sex
names(sex) <- rownames(friendship[[1]])
# step 2: imputation of NAs and removal of absent nodes:
friendship <- xergm.common::handleMissings(friendship, na = 10, method = "remove")
friendship <- xergm.common::handleMissings(friendship, na = NA, method = "fillmode")
# step 3: add nodal covariates to the networks
for (i in 1:length(friendship)) {
s <- xergm.common::adjust(sex, friendship[[i]])
friendship[[i]] <- network::network(friendship[[i]])
friendship[[i]] <- network::set.vertex.attribute(friendship[[i]], "sex", s)
idegsqrt <- sqrt(sna::degree(friendship[[i]], cmode = "indegree"))
friendship[[i]] <- network::set.vertex.attribute(friendship[[i]],
"idegsqrt", idegsqrt)
odegsqrt <- sqrt(sna::degree(friendship[[i]], cmode = "outdegree"))
friendship[[i]] <- network::set.vertex.attribute(friendship[[i]],
"odegsqrt", odegsqrt)
}
sapply(friendship, network::network.size)
net <- friendship
rm(list=setdiff(ls(), "net"))
Once the data is imported and transformed into a list
of network
objects with relevant attributes appended to each network in that list, we can proceed with estimating the ego-TERGM using ego_tergm
. Underneath the hood, quite a bit is occuring when the user calls this function. First, the function transforms the list of longitudinally observed networks into a list of longitudinally observed ego-networks. From that point, the initialization proceedure described by Campbell (2018) is used, including the estimation of a TERGM via bootstrapped pseudolikelihood and subsequent clustering of TERGM parameters using k-means clustering (Campbell 2018). Upon initialization, an Expectation Maximization (EM) algorithm is used to find group-centroids and role assignments.
The ego_tergm
function requires a few arguments:
net
: The list of network objects previously discussed.form
: A vector of comma-seperated ergm-terms
used to distinguish between role assignments.core_size
: The number of alters out from the ego to include and connections among them.min_size
: The minimum size that an ego-network at a particular time period must achieve to be included.roles
: The number of roles to be fit.add_drop
: Whether nodes drop in or drop out of the network.directed
: If the network is directed or not.edge_covariates
: In the form
call, are edge or dyad covariates required?seed
: Seed set for replication.R
: The number of bootstrap replications for the TERGM to be used to generate initial values.parallel
: Should parallel computing be used? If no, specify “no”, otherwise specify “snow” or “multicore”.ncpus
: If parallel computing is to be used, how many CPUs should be used.steps
: The maximum number of steps that the EM process should go through.tol
: The acceptance level to assess the convergence of the EM algorithm.library(egoTERGM)
ego_tergm_fit <- ego_tergm(net = net,
form = c("edges", "mutual", "triangle", "nodeicov('idegsqrt')", "nodeocov('odegsqrt')", "nodematch('sex')"),
core_size = 1,
min_size = 5,
roles = 3,
add_drop = TRUE,
directed = TRUE,
edge_covariates = FALSE,
seed = 12345,
R = 10,
parallel = "no",
ncpus = 1,
steps = 50,
tol = 1e-06)
Once the ego-TERGM is fit using the ego_tergm
function, there are two auxilliary functions that can be used to assist in interpreting model results. First, I have provided an interpret_ego_tegm
function that cleans up the group-based centroids estimated. A warning message is coded into the function that emphasizes that these centroids should not be interpreted as TERGM parameters, but instead, as a means of understanding the distinctions between role assignments. Second, there is a plot_ego_tergm
function that plots the role assignments for the reduced networks. The code is as follows.
interpret_ego_tergm(ego_tergm_fit = ego_tergm_fit)
plots <- plot_ego_tergm(ego_tergm_fit = ego_tergm_fit)
plots[[1]]
To assist in interpreting the role generative structure of role assignments, I have included a simple function, prepare_for_tergm
, that takes the output of an ego_tergm
call and transforms it into a format that can be used by the btergm
function (Leifeld, Cranmer, and Desmarais 2017). The code for this is as follows:
net_list <- prepare_for_tergm(ego_tergm_fit = ego_tergm_fit)
# Indexing of the output for prepare_for_tergm refers to the role numbering from initial ego_tergm_fit
role1_btergm <- btergm(net_list[[1]] ~ edges + mutual + triangle + nodeicov('idegsqrt') +
nodeocov('odegsqrt') + nodematch('sex'),
R = 500)
Campbell, Benjamin W. 2018. “Inferring Latent Roles in Longitudinal Networks.” Political Analysis.
Knecht, Andrea. 2006. “Networks and Actor Attributes in Early Adolescence.” ICS Codebook 61.
Knecht, Andrea B. 2008. Friendship Selection and Friends’ Influence. Dynamics of Networks and Actor Attributes in Early Adolescence. Utrecht University.
Leifeld, Philip, Skyler J Cranmer, and Bruce A Desmarais. 2014. “Xergm: Extensions of Exponential Random Graph Models.” R Package Version 0.51.
———. 2017. “Temporal Exponential Random Graph Models with Btergm: Estimation and Bootstrap Confidence Intervals.” Journal of Statistical Software. Foundation for Open Access Statistics.
Salter-Townshend, Michael, and Thomas Brendan Murphy. 2015. “Role Analysis in Networks Using Mixtures of Exponential Random Graph Models.” Journal of Computational and Graphical Statistics 24 (2). Taylor & Francis: 520–38.