# \dontshow{
require(ergm)
require(network)
require(sna)
set.seed(21093)
a1<-network::as.network(matrix(c(rbinom(10, 1,.3),
rbinom(10, 1,.3),
rbinom(10, 1,.3),
rbinom(10, 1,.3),
rbinom(10, 1,.3),
rbinom(10, 1,.3),
rbinom(10, 1,.3),
rbinom(10, 1,.3),
rbinom(10, 1,.3),
rbinom(10, 1,.3)),
nrow=10,ncol=10))
network::set.vertex.attribute(a1,"var.1",rbinom(10,1,.3))
a<-ergm(a1~edges+nodeifactor("var.1")+nodeofactor("var.1"))
node_data<-data.frame(outdegree=rowSums(network::as.sociomatrix(a1)),
var1=rnorm(10))
b<-lm(var1~outdegree,data=node_data)
AMME(micro_model=a,
macro_model=b,
micro_process="nodeifactor.var.1.1",
mediator="outdegree",
macro_function=function(x){colSums(network::as.sociomatrix(x))},
link_id=1:10,
nsim=20)
# }
# \donttest{
##############################
# Basic AMME specifications
#############################
####create ERGM generative model
library(statnet)
data("faux.mesa.high")
ergm_model<-ergm(faux.mesa.high~edges+
nodecov("Grade")+
nodefactor("Race")+
nodefactor("Sex")+
nodematch("Race")+
nodematch("Sex")+
absdiff("Grade"))
###create node-level data for second stage analysis with
node_level_data<-data.frame(grade=faux.mesa.high%v%"Grade",
race=faux.mesa.high%v%"Race",
sex=faux.mesa.high%v%"Sex",
degree=degree(faux.mesa.high))
node_level_data$senior<-0
node_level_data$senior[node_level_data$grade==max(node_level_data$grade)]<-1
node_level_data$v_id<-1:network.size(faux.mesa.high) #define ID for each observation
probit_model<-glm(senior~race+sex+degree,
data=node_level_data,
family=binomial(link="probit"))
###estimate the indirect effect of grade homophily on senior status acting through degree centrality
#in a model with no network control variables
AMME(micro_model=ergm_model,
macro_model=probit_model,
micro_process="absdiff.Grade",
mediator="degree",
macro_function=degree,
link_id=node_level_data$v_id, #specify vertex IDs
object_type="network",
interval=c(0,1),
nsim=50,
algorithm="parametric",
silent=FALSE)
#use nonparametric estimation for a generalized additive model
library(gam)
gam_model<-gam(senior~race+sex+s(degree),
data=node_level_data)
AMME(micro_model=ergm_model,
macro_model=gam_model,
micro_process="absdiff.Grade",
mediator="s(degree)",
macro_function=degree,
link_id=node_level_data$v_id,
object_type="network",
interval=c(0,1),
nsim=50,
algorithm="nonparametric",
silent=FALSE)
###estimate AMME with linear network autocorrelation model
lnam_model<-lnam(node_level_data$grade,
x=as.matrix(node_level_data[,4:5]),
W1=as.sociomatrix(faux.mesa.high))
AMME(micro_model=ergm_model,
macro_model=lnam_model,
micro_process="absdiff.Grade",
mediator="degree",
macro_function=degree,
link_id=node_level_data$v_id,
object_type="network",
interval=c(0,1),
nsim=50,
algorithm="parametric",
silent=FALSE)
############################
# Including controls
###########################
##single control
node_level_data<-data.frame(grade=faux.mesa.high%v%"Grade",
race=faux.mesa.high%v%"Race",
sex=faux.mesa.high%v%"Sex",
degree=degree(faux.mesa.high),
betweenness=betweenness(faux.mesa.high))
node_level_data$senior<-0
node_level_data$senior[node_level_data$grade==max(node_level_data$grade)]<-1
node_level_data$v_id<-1:network.size(faux.mesa.high) #define ID for each observation
probit_model<-glm(senior~race+sex+degree+betweenness,
data=node_level_data,
family=binomial(link="probit"))
AMME(micro_model=ergm_model,
macro_model=probit_model,
micro_process="absdiff.Grade",
mediator="degree",
macro_function=degree,
link_id=node_level_data$v_id, #specify vertex IDs
controls="betweenness", #should match model output exactly
control_functions=betweenness,
object_type="network",
interval=c(0,1),
nsim=50,
algorithm="parametric",
silent=FALSE)
##multiple controls
##include an AR 1 parameter to make it a nonlinear network autocorrelation model
node_level_data$AR1<-as.sociomatrix(faux.mesa.high)%*%node_level_data$senior
probit_model<-glm(senior~race+sex+degree+betweenness+AR1,
data=node_level_data,
family=binomial(link="probit"))
#specify user function
ar_function<-function(x){
return(as.sociomatrix(x)%*%node_level_data$senior)
}
AMME(micro_model=ergm_model,
macro_model=probit_model,
micro_process="absdiff.Grade",
mediator="degree",
macro_function=degree,
link_id=node_level_data$v_id,
controls=c("betweenness","AR1"), #should match model output exactly
control_functions=list(betweenness,ar_function), #provide functions as a list
object_type="network",
interval=c(0,1),
nsim=50,
algorithm="parametric",
silent=FALSE)
##using identity_function when micro_process has a direct effect on y
#to use identity_function, the control and micro_process need to have the same
#name and the macro control variable has to be numeric
node_level_data$Sex<-as.numeric(as.factor(node_level_data$sex))
logit_model<-glm(senior~race+Sex+degree+betweenness+AR1,
data=node_level_data,
family=binomial)
AMME(micro_model=ergm_model,
macro_model=logit_model,
micro_process="nodefactor.Sex.M",
mediator="degree",
macro_function=degree,
link_id=node_level_data$v_id,
controls=c("betweenness","AR1","Sex"), #should match model output exactly
control_functions=list(betweenness,ar_function,identity_function),
object_type="network",
interval=c(0,1),
nsim=50,
algorithm="parametric",
silent=FALSE)
################################
# More complex data structures
###############################
###############################
# AMME with longitudinal data
##############################
#bootstrap TERGM and panel data model
library(btergm)
library(plm)
data(alliances)
ally_data<-list(LSP[[1]],
LSP[[2]],
LSP[[3]])
#fit bootstrap TERGM with 200 replications
bt_model<-btergm(ally_data~edges+
gwesp(.7,fixed=T)+
mutual,R=200)
#create node data
ally_node_data<-data.frame(outdeg=c(rowSums(LSP[[1]]),rowSums(LSP[[2]]),rowSums(LSP[[3]])),
indeg=c(colSums(LSP[[1]]),colSums(LSP[[2]]),colSums(LSP[[3]])))
ally_node_data$v_id<-rep(rownames(LSP[[1]]),3) #create node IDS
ally_node_data$t_id<-c(rep(1, nrow(ally_data[[1]])), #create time IDS
rep(2, nrow(ally_data[[1]])),
rep(3, nrow(ally_data[[1]])))
ally_node_data$link_id<-paste(ally_node_data$v_id,ally_node_data$t_id)#create node-panel identifiers
ally_node_data$v_id<-as.factor(as.character(ally_node_data$v_id))
#estimate a linear model with node fixed effects
lm_model<- lm(outdeg~indeg +v_id,
data = ally_node_data)
AMME(micro_model=bt_model,
macro_model=lm_model,
micro_process="gwesp.OTP.fixed.0.7",
mediator="indeg",
macro_function=function(x){degree(x,cmode="indegree")},
link_id=ally_node_data$link_id, #provide node-panel identifiers
object_type="network",
interval=c(0,1),
nsim=11,
algorithm="nonparametric",
silent=FALSE)
##include controls at different units of analysis
#include global transitivity statistic at each network panel
transitivity_list<-c(gtrans(as.network(LSP[[1]])),
gtrans(as.network(LSP[[2]])),
gtrans(as.network(LSP[[3]])))
ally_node_data$transitivity<-c(rep(transitivity_list[1],nrow(LSP[[1]])),
rep(transitivity_list[2],nrow(LSP[[2]])),
rep(transitivity_list[3],nrow(LSP[[3]])))
lm_model<- lm(outdeg~indeg+transitivity +v_id,
data = ally_node_data)
AMME(micro_model=bt_model,
macro_model=lm_model,
micro_process="gwesp.OTP.fixed.0.7",
mediator="indeg",
macro_function=function(x){degree(x,cmode="indegree")},
link_id=list(ally_node_data$link_id,ally_node_data$t_id),#list of IDs for nodes and time
controls="transitivity",
control_functions = gtrans,
object_type="network",
interval=c(0,1),
nsim=11,
algorithm="nonparametric",
silent=FALSE)
#SAOM and panel data model with PLM package
library(RSiena)
#specify 3 wave network panel data as DV
network_list<-array(c(s501,s502,s503),dim = c(50,50,3))
Network<-sienaDependent(network_list)
Smoking<-varCovar(s50s)
Alcohol<-varCovar(s50a)
SAOM.Data<-sienaDataCreate(Network=Network,Smoking,Alcohol)
#specify
SAOM.terms<-getEffects(SAOM.Data)
SAOM.terms<-includeEffects(SAOM.terms,egoX,altX,sameX,interaction1="Alcohol")
SAOM.terms<-includeEffects(SAOM.terms,egoX,altX,sameX,interaction1="Smoking")
SAOM.terms<-includeEffects(SAOM.terms,transTies,inPop)
create.model<-sienaAlgorithmCreate(projname="netmediate",
nsub=5,
n3=2000)
##estimate the SAOM
SAOM_model<-siena07(create.model,
data=SAOM.Data,
effects=SAOM.terms,
verbose=TRUE)
##create node-level data
node_level_data<-data.frame(smoking=s50s[,1], #smoking behavior for DV
alcohol=s50a[,1],
v_id=rownames(s501), #unique node IDS
wave="Wave 1", #unique time IDS
outdegree=rowSums(s501),
indegree=colSums(s501),
AR1=s501%*%s50s[,1], #assign network autocorrelation
gcc=gtrans(as.network(s501)))
node_level_data<-rbind(node_level_data,data.frame(smoking=s50s[,2],
alcohol=s50a[,2],
v_id=rownames(s502),
wave="Wave 2",
outdegree=rowSums(s502),
indegree=colSums(s502),
AR1=s502%*%s50s[,2],
gcc=gtrans(as.network(s502))))
node_level_data<-rbind(node_level_data,data.frame(smoking=s50s[,3],
alcohol=s50a[,3],
v_id=rownames(s503),
wave="Wave 3",
outdegree=rowSums(s503),
indegree=colSums(s503),
AR1=s503%*%s50s[,3],
gcc=gtrans(as.network(s503))))
##create unique identifiers for node-panel
node_level_data$unique_ids<-paste(node_level_data$v_id,node_level_data$wave)
##estimate one-way fixed effects model with PLM
library(plm)
FE_model<-plm(smoking~alcohol+outdegree+indegree+AR1+gcc,
data=node_level_data,
index=c("v_id","wave"))
##create AR function to provide to AMME
ar_function<-function(x){return(as.sociomatrix(x)%*%(x%v%"Smoking"))}
AMME(micro_model=SAOM_model,
macro_model=FE_model,
micro_process="reciprocity",
mediator="indegree",
macro_function=function(x){degree(x,cmode="indegree")},
link_id=list(node_level_data$unique_id,node_level_data$unique_id,
node_level_data$unique_id,node_level_data$wave),
object_type="network",
controls=c("outdegree","AR1","gcc"),
control_functions=list(function(x){degree(x,cmode="outdegree")},ar_function,gtrans),
interval=c(0,.1),
nsim=500,
algorithm="parametric",
silent=FALSE,
SAOM_data = SAOM.Data,
SAOM_var=list(Smoking=Smoking,Alcohol=Alcohol)) #provide var_list
################################
# AMME with pooled ERGM and SAOM
################################
#pooled ERGM
#fit two ERGMs to two networks
data("faux.mesa.high")
model1<-ergm(faux.mesa.high~edges+
nodecov("Grade")+
nodefactor("Race")+
nodefactor("Sex")+
nodematch("Race")+
nodematch("Sex")+
absdiff("Grade"))
data("faux.magnolia.high")
model2<-ergm(faux.magnolia.high~edges+
nodecov("Grade")+
nodefactor("Race")+
nodefactor("Sex")+
nodematch("Race")+
nodematch("Sex")+
absdiff("Grade"))
#create node level data
node_level_data<-data.frame(grade=faux.mesa.high%v%"Grade",
sex=faux.mesa.high%v%"Sex",
degree=degree(faux.mesa.high),
betweenness=betweenness(faux.mesa.high),
gcc=gtrans(faux.mesa.high),
net_id="Mesa")
node_level_data$senior<-0
node_level_data$senior[node_level_data$grade==max(node_level_data$grade)]<-1
node_level_data$v_id<-1:network.size(faux.mesa.high)
node_level_data2<-data.frame(grade=faux.magnolia.high%v%"Grade",
sex=faux.magnolia.high%v%"Sex",
degree=degree(faux.magnolia.high),
betweenness=betweenness(faux.magnolia.high),
gcc=gtrans(faux.magnolia.high),
net_id="Magnolia")
node_level_data2$senior<-0
node_level_data2$senior[node_level_data$grade==max(node_level_data2$grade)]<-1
node_level_data2$v_id<-206:(network.size(faux.magnolia.high)+205)
node_level_data<-rbind(node_level_data,node_level_data2)
#estimate glm macro model with an AR 1 process
probit_model<-glm(senior~sex+degree+betweenness+gcc,
data=node_level_data,
family=binomial(link="probit"))
AMME(micro_model=list(model1,model2),
macro_model=probit_model,
micro_process="nodematch.Sex",
mediator="degree",
macro_function=degree,
link_id=list(node_level_data$v_id,node_level_data$v_id,node_level_data$net_id),
object_type="network",
controls=c("betweenness","gcc"),
control_functions=list(betweenness,gtrans),
interval=c(0,1),
nsim=50,
algorithm="parametric",
silent=FALSE)
##pooled SAOM with control functions using time varying covariates
library(RSiena)
#specify 3 wave network panel data as DV
network_list<-array(c(s501,s502,s503),dim = c(50,50,3))
Network<-sienaDependent(network_list)
Smoking<-varCovar(s50s)
Alcohol<-varCovar(s50a)
SAOM.Data<-sienaDataCreate(Network=Network,Smoking,Alcohol)
#specify
SAOM.terms<-getEffects(SAOM.Data)
SAOM.terms<-includeEffects(SAOM.terms,egoX,altX,sameX,interaction1="Alcohol")
SAOM.terms<-includeEffects(SAOM.terms,egoX,altX,sameX,interaction1="Smoking")
SAOM.terms<-includeEffects(SAOM.terms,transTies,inPop)
create.model<-sienaAlgorithmCreate(projname="netmediate",
nsub=5,
n3=2000)
##estimate the SAOM
SAOM_model<-siena07(create.model,
data=SAOM.Data,
effects=SAOM.terms,
verbose=TRUE)
##create node-level data
node_level_data<-data.frame(smoking=s50s[,1], #smoking behavior for DV
alcohol=s50a[,1],
v_id=rownames(s501), #unique node IDS
wave="Wave 1", #unique time IDS
outdegree=rowSums(s501),
indegree=colSums(s501),
AR1=s501%*%s50s[,1], #assign network autocorrelation
gcc=gtrans(as.network(s501)))
node_level_data<-rbind(node_level_data,data.frame(smoking=s50s[,2],
alcohol=s50a[,2],
v_id=rownames(s502),
wave="Wave 2",
outdegree=rowSums(s502),
indegree=colSums(s502),
AR1=s502%*%s50s[,2],
gcc=gtrans(as.network(s502))))
node_level_data<-rbind(node_level_data,data.frame(smoking=s50s[,3],
alcohol=s50a[,3],
v_id=rownames(s503),
wave="Wave 3",
outdegree=rowSums(s503),
indegree=colSums(s503),
AR1=s503%*%s50s[,3],
gcc=gtrans(as.network(s503))))
#recycle the same model for illustrative purposes
node_level_data$net_ID<-"Model 1"
node_level_data<-rbind(node_level_data,node_level_data)
node_level_data$net_ID[151:300]<-"Model 2"
##create unique identifiers for node-panel
#ID for node-panel-model
node_level_data$unique_id<-paste(node_level_data$v_id,node_level_data$wave,node_level_data$net_ID)
#ID for panel-model
node_level_data$unique_waves<-paste(node_level_data$wave,node_level_data$net_ID)
#estimate a linear network autocorrelation model with node fixed effects
FE_model<-lm(smoking~alcohol+outdegree+indegree+AR1+gcc+v_id,
data=node_level_data)
##create user function calculate AR1 process on time varying node attributes
ar_function<-function(x){return(as.sociomatrix(x)%*%(x%v%"Smoking"))}
##estimate AMME
AMME(micro_model=list(SAOM_model,SAOM_model), #provide list of sienaFit objects
macro_model=FE_model,
micro_process="reciprocity",
mediator="indegree",
macro_function=function(x){degree(x,cmode="indegree")},
link_id=list(node_level_data$unique_id,node_level_data$unique_id,
node_level_data$unique_id,node_level_data$unique_waves),
object_type="network",
controls=c("outdegree","AR1","gcc"),
control_functions=list(function(x){degree(x,cmode="outdegree")},ar_function,gtrans),
interval=c(0,.1),
nsim=100, #parametric estimation requires more simulations than coefficients
algorithm="parametric",
silent=FALSE,
SAOM_data = list(SAOM.Data,SAOM.Data), #list of siena objects
SAOM_var=list(list(Smoking=Smoking,Alcohol=Alcohol),#provide var_list
list(Smoking=Smoking,Alcohol=Alcohol)))
#################################
# AMME with nested data
################################
####create dyad-level data
library(lme4)
library(btergm)
##use small data to simplify estimation
glm_dat<-edgeprob(model1)
glm_dat$net_id<-"mesa"
glm_dat2<-edgeprob(model2)
glm_dat2$net_id<-"magnolia"
glm_dat<-rbind(glm_dat,glm_dat2[,-c(4)])
##estimate micro model as glm for btoh networks using pooled ERGM data
net_glm<-glm(tie~nodecov.Grade+
nodefactor.Race.Hisp+
nodefactor.Race.NatAm+
nodefactor.Race.Other+
nodefactor.Sex.M+
nodematch.Race+
nodematch.Sex+
absdiff.Grade,
data=glm_dat)
#create macro data
node_level_data<-data.frame(grade=faux.mesa.high%v%"Grade",
sex=faux.mesa.high%v%"Sex",
degree=degree(faux.mesa.high),
betweenness=betweenness(faux.mesa.high),
gcc=gtrans(faux.mesa.high),
net_id="Mesa")
node_level_data$senior<-0
node_level_data$senior[node_level_data$grade==max(node_level_data$grade)]<-1
node_level_data$v_id<-1:network.size(faux.mesa.high)
node_level_data2<-data.frame(grade=faux.magnolia.high%v%"Grade",
sex=faux.magnolia.high%v%"Sex",
degree=degree(faux.magnolia.high),
betweenness=betweenness(faux.magnolia.high),
gcc=gtrans(faux.magnolia.high),
net_id="Magnolia")
node_level_data2$senior<-0
node_level_data2$senior[node_level_data$grade==max(node_level_data2$grade)]<-1
node_level_data2$v_id<-206:(network.size(faux.magnolia.high)+205)
node_level_data<-rbind(node_level_data,node_level_data2)
#estimate glm macro model
probit_model<-glm(senior~sex+degree+betweenness+gcc,
data=node_level_data,
family=binomial(link="probit"))
AMME(micro_model=net_glm,
macro_model=probit_model,
micro_process="nodematch.Sex",
mediator="degree",
macro_function=degree,
link_id=list(node_level_data$v_id,node_level_data$v_id,node_level_data$net_id),
object_type="network",
controls=c("betweenness","gcc"),
control_functions=list(betweenness,gtrans),
interval=c(0,1),
nsim=50,
algorithm="parametric",
silent=FALSE,
group_id=glm_dat$net_id,
node_numbers = c(network.size(faux.mesa.high),
network.size(faux.magnolia.high)))
###using glmer for micro model
net_glmer<-glmer(tie~nodecov.Grade+
nodefactor.Race.Hisp+
nodefactor.Race.NatAm+
nodefactor.Race.Other+
nodefactor.Sex.M+
nodematch.Race+
nodematch.Sex+
absdiff.Grade+
(1|net_id),
data=glm_dat)
probit_glmer<-glm(senior~sex+degree+betweenness+gcc,
data=node_level_data,
family=binomial(link="probit"))
AMME(micro_model=net_glm,
macro_model=probit_glmer,
micro_process="nodematch.Sex",
mediator="degree",
macro_function=degree,
link_id=list(node_level_data$v_id,node_level_data$v_id,node_level_data$net_id),
object_type="network",
controls=c("betweenness","gcc"),
control_functions=list(betweenness,gtrans),
interval=c(0,1),
nsim=50,
algorithm="parametric",
silent=FALSE,
group_id=glm_dat$net_id,
node_numbers = c(network.size(faux.mesa.high),
network.size(faux.magnolia.high)))
# }
Run the code above in your browser using DataLab