For the Unit 4 Case Study: Birds of Feather Lead Together, we once again visit the work of Alan Daly and colleagues as we attempt to replicate some of the analyses described in Chapter 9: Network Data and Statistical Models from Social Network Analysis and Education (Carolan 2014). In this case study move beyond the visual and mathematical descriptions of networks explored so far to uncover generative processes, or mechanisms, that attempt to explain how school leaders select peers for collaboration or confidential exchanges.
More specifically, this case study will cover the following topics pertaining to each data-intensive workflow process:
Prepare: Prior to analysis, we’ll take a look at the context from which our data came, formulate some research questions, and get introduced the {statnet} R package for ERGMs.
Wrangle: In section 2 we will learn how to work with matrices, how to dichotomize matrices, and how to convert matrices into more workable formats like our familiar edge-list.
Explore: In section 3, we once again use the {tidygraph} package and the companion {ggraph} package to calculate basic summary stats for our network and will try to replicate and ideally improve upon the sociogram from our course text.
Model: We wrap up our analysis in Section 4 by introducing exponential random graph modules used in Chapter 9 of Carolan (2014), learn to check how well our model “fits our data” and examine diagnostics that might indicate issues with our model.
Communicate: Finally, we’ll select, polish, and narrate a key finding from our analysis that helps to answer or research question(s).
In Social Network Analysis and Education: Theory, Methods & Applications Carolan (2014) makes the following distinctions between mathematical and statistical approaches to social network analysis:
Mathematical approaches focus on what a network of actors “looks like” by describing the network using sociograms and/or network measures such as reciprocity, centrality, and density. However, these approaches “tend to regard the measured relationships and their strengths as accurately reflecting the real, final, or equilibrium status of the network.”
Statistical approaches, or statistical inference, on the other hand, focuses on “assessing the reproducibility or likelihood of an observed pattern” with the goal of explaining and ultimately predicting network structures and outcomes.
For Unit 4, we move beyond previous “mathematical approaches” used so far and explore the use of ERGMs as a “statistical approach” to network analysis. Specifically, we will try to replicate the P* models used in Chapter 9 of Carolan (2014) in order to test hypotheses about individual and network characteristics that may explain who school leaders select for collaboration and confidential exchanges.
For this case study, we will be using school leadership data were collected at two school districts over 3 consecutive years that was used to answer the following research questions below:
Of specific interest for our case study is research question 3 emphasized in bold text above, which asks whether individual attributes predicts tie formation. For example, are males more likely to engage in exchange confidential exchanges with other.
The tendency for individuals to be attracted to and interact with others who share similar characteristics is formally referred to as homophily or assortativity, but more commonly known by the “birds of a feather flock together” proverb.
Homophily has been well documented in education research across a variety of relation types including student friendships, teacher-pupil interactions, and membership in student cohorts (McPherson and Smith-Lovin 1987). Studies of homophily among student friendships, for example, date back to the 1920’s and provide strong evidence that students form ties based on sociodemographic similarities.
For each consecutive year, school district leaders were invited to complete a survey that collected individual:
Demographic information (e.g., gender, ethnicity, marital status, age, years of experiences);
Network relationships types (e.g., collaboration, confidential exchanges, energy, expertise, leaders approached for support, work-related issues, input, recognition, best practices, and innovation);
Frequency of interactions they have with those nominated individuals on a four-point frequency scale ranging from 1 (the least frequent) to 4 (1–2 times a week);
Leadership efficacy items were designed based on the Principal Efficacy Scale used in Daly et al. (2011) and Tschannen-Moran and Gareis’s (2004) studies. The efficacy scale includes 18 items rated on a 9-point Likert scale ranging from 1 (None at all) to 9 (A great deal);
Trust scale contains eight items rated on a 7-point Likert scale ranging from 1 (Strongly disagree) to 7 (Strongly agree) modified from Tschannen-Moran and Hoy (2003).
Two modeling techniques unique to network analysis and demonstrated in Chapter 9 of Carolan (2014) are the quadratic assignment procedure (QAP) and Exponential Random Graph Models (ERGM):
QAP/MR: The quadratic assignment procedure (QAP) developed by Hubert (1987) and Krackhardt (1987b) tests the null hypothesis of no correlation between the two networks and adjusts for this dependence between networks by repeatedly permuting the order of rows and columns of one of the networks while keeping the other network intact. The QAP is based on regression models and permutation tests for valued (i.e., continuous) relational variables.
P1 and P* (P-Star): Both of these models are some of the first to make use of the ERGM, which provides a basis for comparing whether a network’s observed structural properties occur more frequently than you could expect from chance alone. ERGMs can be used to model ties in complete networks but do so in a manner that explains the presence (or absence) of ties as a function of individual-and/or network-level characteristics. While QAP and MR-QAP procedures control for network structure through permutations, these ERGMs attempt to explain it.
In response to research questions 1 & 2 from above, Carolyn reported that “while collaboration in year 1 does not significantly predict collaboration in year 3, confidential exchanges in year 1 does” and suggested that “collaboration among school leaders provides an important foundation for more sensitive, perhaps even deeper, relations (e.g., confidential exchanges) at a later point in time.”
For our Unit 4 case study, we focus specifically on generative processes, or micro-level mechanisms, the might help explain who school leaders select to collaborate with or engage in confidential exchanges. Review Chapter 9 of SNA and Education and report out on the findings from the ERGM analysis in response to Research Question 3 below:
Does gender or some other individual attribute predicts confidential exchanges between school leaders, or does some previous relation have a stronger effect?
Based on what you know about networks and the context so far, write another research question or hypothesis that can be tested using ERGMs:
Later you’ll have a chance to empirically test it by the end of this case study.
Recall that packages, or libraries, are shareable collections of R code that can contain functions, data, and/or documentation and extend the functionality of R. You can always check to see which packages have already been installed and loaded into RStudio Cloud by looking at the the Files, Plots, & Packages Pane in the lower right hand corner.
Similar to the collection of packages contained in the {tidyverse} package, the Statnet Team (Pavel N. Krivitsky et al. 2003-2020) has developed a suite of R packages for the management, exploration, statistical analysis, simulation and vizualization of network data. The statistical modeling framework used in {statnet} relies on Exponential-family Random Graph Models (ERGMs).
As noted in the statnet tutorial by the same authors, the exponential-family random graph models (ERGMs) are a general class of models based in exponential-family theory for specifying the probability distribution for a set of random graphs or networks. Within this framework, one can—among other tasks:
Define a model for a network that includes covariates representing features like homophily, mutuality, triad effects, and a wide range of other structural features of interest;
Obtain maximum-likehood estimates for the parameters of the specified model for a given data set;
Test individual coefficients, assess models for convergence and goodness-of-fit and perform various types of model comparison; and
Simulate new networks from the underlying probability distribution implied by the fitted model.
Let’s load the {statnet} package that we’ll be using in the Model section of our case study for bullets 1-3 above, as well as the following packages that will be using for our network analysis:
library(knitr)
library(tidyverse)
library(janitor)
library(skimr)
library(ggraph)
library(igraph)
library(tidygraph)
library(readxl)
library(network)
library(ergm)
library(kableExtra)
library(texreg)
For our data wrangling this week, we’ll focus on working with network data stored as an adjacency matrix. Our primary goals for this section are learning how to:
Import Data. In this section, we use the {readxl} package from the tidyverse to read in a matrix and node attributes.
Dichotomize a Matrix. As described in Chapter 9, we’ll recode our edge values to 1s and 0s changing our valued matrix to a binary matrix.
Create Network Graph. Finally, we’ll convert our matrix to an edge-list and store both our edges and node attributes as a network igraph object in preparation for analysis.
One of our primary goals is to replicate the ERGM analysis from Chapter 9: Network Data and Statistical Models (Carolan 2014). Specifically, we’ll aim to reproduce the results from Table 9.4. Results of P1 and P*Analyses for the Dichotomized and Directed School Leaders Confidential Exchanges Network Year 3.
To do so, we’ll need to import two Excel files from the Social Network Analysis and Education companion site. The first file contains our edges stored as a square matrix (more on this later) and the second file is a standard rectangular data frame that contains attributes for each node, i.e. school leaders. These files are included in the data folder of your R Studio project. A description of each file from the companion website is linked above and each data file is linked below:
School Leaders Data Chapter 9_d. This adjacency matrix reports on “confidential help” ties among 43 school leaders in year 3 of a three-year study. This is a directed valued (weighted) network measured on five-point scale ranging from 0 to 4, with higher values indicating more frequent collaborations (1–2 times/week). These data are used throughout Chapter 9.
School Leaders Data Chapter 9_e. This rectangular matrix consists of four attribute vectors for 43 school leaders. Following the first ID column, the matrix includes an efficacy score, trust score, and indicators for whether one works at the district-level and is male (1 = yes, 0 = no). These attribute variables will be used as covariates in our ERGMs later in this walkthrough.
Since we are working with Excel files, we’ll need to use the
read_excel()
function from {readxl} tidyverse package to
import our data. Let’s import the
School Leaders Data Chapter 9_e.xlsx
node file located in
the data/
folder first:
leader_nodes <- read_excel("data/School Leaders Data Chapter 9_e.xlsx",
col_types = c("text", "numeric", "numeric", "numeric", "numeric")) |>
clean_names()
leader_nodes
## # A tibble: 43 × 5
## id efficacy trust district_site male
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 1 6.06 4 1 0
## 2 2 6.56 5.63 1 0
## 3 3 7.39 4.63 1 0
## 4 4 4.89 4 1 0
## 5 5 6.06 5.75 0 1
## 6 6 7.39 4.38 0 0
## 7 7 5.56 3.63 0 1
## 8 8 7.5 5.63 1 1
## 9 9 7.67 5.25 0 0
## 10 10 6.64 4.78 0 0
## # … with 33 more rows
Note that we specified the ID
column as “text” and the
remaining columns as “numeric.” By default, the readxl()
function would have recognized each column as numeric, but the first
column indicates the “names” for each school leader and we’ll be using
this column to assign names to our columns and rows for our adjacency
matrix in just a bit.
Recall from above that our relations, or edges, are stored as a valued adjacency matrix in which columns and rows consist of the same actors and each cell contains information about the tie between each pair of actors. In our case, the tie is a directed and valued “arc” where the value indicates the strength of the relationship.
Use the read_excel()
function to import the
School Leaders Data Chapter 9_d.xlsx
file, add an argument
setting the column names to FALSE
since our file is a
simple matrix with no header or column names, and assign the matrix to a
variable named leader_matrix
:
Hint: Type ?read_excel
into the console
and check the arguments section to find the name of the argument used to
set column headers.
leader_matrix <- read_excel("data/School Leaders Data Chapter 9_d.xlsx", col_names = FALSE)
Use the code chunk below to inspect the matrix you just imported and answer the questions that follow:
leader_matrix
## # A tibble: 43 × 43
## ...1 ...2 ...3 ...4 ...5 ...6 ...7 ...8 ...9 ...10 ...11 ...12 ...13
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 0 0 0 0 0 0 0 0 0 0 0 0 0
## 2 0 0 0 0 0 0 0 0 0 0 0 0 0
## 3 3 1 0 1 1 1 1 2 1 1 1 1 1
## 4 1 0 0 0 0 0 0 0 0 0 0 0 0
## 5 0 0 0 0 0 0 1 0 3 0 0 0 0
## 6 0 0 0 0 0 0 0 0 0 0 0 0 0
## 7 2 1 1 1 1 1 4 1 1 3 1 1 1
## 8 1 1 4 1 1 1 1 0 2 3 1 1 1
## 9 1 0 0 0 4 0 0 1 0 0 0 0 0
## 10 1 1 3 1 1 1 4 4 1 0 3 1 1
## # … with 33 more rows, and 30 more variables: ...14 <dbl>, ...15 <dbl>,
## # ...16 <dbl>, ...17 <dbl>, ...18 <dbl>, ...19 <dbl>, ...20 <dbl>,
## # ...21 <dbl>, ...22 <dbl>, ...23 <dbl>, ...24 <dbl>, ...25 <dbl>,
## # ...26 <dbl>, ...27 <dbl>, ...28 <dbl>, ...29 <dbl>, ...30 <dbl>,
## # ...31 <dbl>, ...32 <dbl>, ...33 <dbl>, ...34 <dbl>, ...35 <dbl>,
## # ...36 <dbl>, ...37 <dbl>, ...38 <dbl>, ...39 <dbl>, ...40 <dbl>,
## # ...41 <dbl>, ...42 <dbl>, ...43 <dbl>
Is our network a square matrix? What does this tell you about the number of nodes in our network?
Does our network contain self-loops? How do you know?
What do the values in each cell of our matrix indicate?
Since one of our goals is a crude replication of the analysis demonstrated in Chapter 9, and since working with “valued networks” is a little more complex than binary networks that simply indicate the presence or absence of a tie, we will need to “dichotomize” our matrix. In practice this simply entails converting our valued matrix to a binary matrix with cells containing just 1s or 0s.
In their article, Valued Ties Tell Fewer Lies: Why Not To Dichotomize Network Edges With Thresholds, Thomas and Blitzstein (2018) highlight several reasons why the dichotomization procedure is appealing in an investigation aside from convenience and simplicity:
Use of Exclusively Binary Methods. Several classes of models have been designed to incorporate binary information directly, including the exponential random graph model.
Ease of Input and Data Collection. The need to classify continuously-valued quantities into a set of discrete groups is widespread throughout all of science and technology, particularly because of the associated need to make clear decisions based on this information.
Ease of Output in Graphical Representations. The visual appeal of graphs and networks has contributed to much of the field’s attention in the past decade. When plotting a graphical structure, a clever choice of threshold can illuminate which nodes are most central, which connections the most vital.
Sparsity of Structure. In data where there are very few natural zeroes (if any), di- chotomization provides a way to select for a small number of connections which are thought to be of the greatest importance to the system, or to nominate a number of ties for more in-depth study.
Binning To Address Nonlinearity and Reduce Noise. If there is a nonlinear relationship in the data, binning the data into distinct ordinal categories has many advantages, namely, the reduction of total mean-squared error, and a corresponding increase in power for detecting a true non-zero relationship over an improperly specified linear analysis.
As the title of their article suggests, however, Thomas and Blitzstein (2011) argue that the motivations for dichotomization should be revisited as dichotomization produces a range of problematic issues.
With that in mind, and before we can dichotomize our “matrix,” we
first need to convert it to a matrix object recognized by R using the
as.matrix()
function. We can then check to data format of
our leader_matrix
using the class()
function:
leader_matrix <- leader_matrix |>
as.matrix()
class(leader_matrix)
## [1] "matrix" "array"
Both the collaboration and confidential help network data were dichotomized by recoding values originally coded as a 3 and 4 recoded to 1, indicating the presence of a directed tie for both relations, and zero otherwise.
To dichotomize our our matrix, the following code will “assign” 0’s to all values in our matrix that are less than or equal to 2, and 1’s to all values that are greater the or equal to 3:
leader_matrix[leader_matrix <= 2] <- 0
leader_matrix[leader_matrix >= 3] <- 1
Before we can convert to an edge-list, we will also need to add the
names of our nodes to the columns and rows of our matrix. These are
stored in the ID
column of our leader_node
data frame. We can use the $
operator to select these names
and assign to our leader_matrix
using the
rownames()
and colnames()
functions
respectively:
rownames(leader_matrix) <- leader_nodes$id
colnames(leader_matrix) <- leader_nodes$id
leader_matrix
## 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
## 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 3 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1
## 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1
## 5 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 7 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0
## 8 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 1
## 9 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 10 0 0 1 0 0 0 1 1 0 0 1 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0
## 11 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0
## 12 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 13 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 14 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 15 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
## 16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
## 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0
## 18 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 19 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
## 20 0 0 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 21 1 0 0 0 0 0 0 0 0 0 1 1 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0
## 22 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1
## 23 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 24 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
## 25 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 26 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0
## 27 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
## 28 1 1 1 1 0 1 0 1 1 1 0 0 1 0 0 0 0 1 0 0 1 1 0 0 1 0 1 1
## 29 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 30 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 31 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
## 32 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
## 33 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
## 34 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 1
## 35 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
## 36 1 0 1 1 0 0 0 1 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 1 1 1
## 37 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 38 1 0 1 0 0 0 0 1 0 0 1 0 0 0 0 0 0 1 0 0 0 1 0 0 0 1 1 1
## 39 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 40 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 41 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 42 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 1 0
## 43 0 0 0 0 1 0 0 1 0 0 0 0 0 0 1 0 0 1 0 0 1 1 1 0 0 0 0 0
## 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
## 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 3 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0
## 4 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0
## 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 8 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0
## 9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 10 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0
## 11 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0
## 12 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 13 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0
## 14 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
## 15 0 0 0 0 0 0 0 0 0 0 1 0 0 1 1
## 16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 18 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0
## 19 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0
## 20 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 21 0 0 0 0 1 0 1 0 1 1 0 0 0 0 1
## 22 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 23 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 24 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
## 25 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 26 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0
## 27 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0
## 28 1 0 0 1 0 1 0 1 1 1 0 0 0 0 0
## 29 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 30 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 31 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 32 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 33 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 34 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 35 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 36 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
## 37 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0
## 38 0 0 0 0 1 1 0 1 1 0 1 0 0 0 1
## 39 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 40 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 41 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0
## 42 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1
## 43 0 0 0 1 1 1 0 0 1 0 0 0 0 1 0
Recall that edge-lists contain a row for each dyad consisting of at minimum two columns with the name of each actor, and which conveniently can also contain other information or attributes about the relationship such as edge weight, timestamps, or other contextual information as demonstrated in Unit 1.
Edge-lists also have the advantage of being easier to work with when using network packages in R.
The {igraph} package introduced in Unit 1 has a convenient
get.data.frame()
function for extracting an edge list from
a matrix, but first we need to convert our matrix to an igraph network
object.
adjacency_matrix <- graph.adjacency(leader_matrix,
diag = FALSE)
class(adjacency_matrix)
## [1] "igraph"
adjacency_matrix
## IGRAPH 09dde33 DN-- 43 143 --
## + attr: name (v/c)
## + edges from 09dde33 (vertex names):
## [1] 3 ->1 3 ->27 3 ->28 3 ->36 3 ->38 4 ->16 4 ->28 4 ->34 4 ->36 5 ->9
## [11] 7 ->10 7 ->20 8 ->3 8 ->10 8 ->25 8 ->27 8 ->28 8 ->37 8 ->38 9 ->5
## [21] 9 ->14 10->3 10->7 10->8 10->11 10->18 10->20 10->38 11->10 11->21
## [31] 11->23 11->35 13->8 13->35 13->42 14->37 15->21 15->39 15->42 15->43
## [41] 16->22 17->26 18->29 18->34 19->31 20->7 20->10 21->1 21->11 21->12
## [51] 21->15 21->33 21->35 21->37 21->38 21->43 22->8 22->16 22->28 23->5
## [61] 23->11 24->27 24->42 25->3 25->8 26->17 26->41 27->24 27->32 27->42
## [71] 28->1 28->2 28->3 28->4 28->6 28->8 28->9 28->10 28->13 28->18
## + ... omitted several edges
Note that we included the diag = FALSE
argument which
converts all values along the diagonal to 0s, thereby removing
self-loops. I believe they may have been included when calculating the
network descriptives included in Table 9.1, and our own descriptives may
not match exactly, but we’ll need to remove these from our network for
the ERGM analysis so this will save us a step.
Now we can use the get.data.frame()
function to covert
our matrix to a standard edge-list:
leader_edges <- get.data.frame(adjacency_matrix) |>
mutate(from = as.character(from)) |>
mutate(to = as.character(to))
leader_edges
## from to
## 1 3 1
## 2 3 27
## 3 3 28
## 4 3 36
## 5 3 38
## 6 4 16
## 7 4 28
## 8 4 34
## 9 4 36
## 10 5 9
## 11 7 10
## 12 7 20
## 13 8 3
## 14 8 10
## 15 8 25
## 16 8 27
## 17 8 28
## 18 8 37
## 19 8 38
## 20 9 5
## 21 9 14
## 22 10 3
## 23 10 7
## 24 10 8
## 25 10 11
## 26 10 18
## 27 10 20
## 28 10 38
## 29 11 10
## 30 11 21
## 31 11 23
## 32 11 35
## 33 13 8
## 34 13 35
## 35 13 42
## 36 14 37
## 37 15 21
## 38 15 39
## 39 15 42
## 40 15 43
## 41 16 22
## 42 17 26
## 43 18 29
## 44 18 34
## 45 19 31
## 46 20 7
## 47 20 10
## 48 21 1
## 49 21 11
## 50 21 12
## 51 21 15
## 52 21 33
## 53 21 35
## 54 21 37
## 55 21 38
## 56 21 43
## 57 22 8
## 58 22 16
## 59 22 28
## 60 23 5
## 61 23 11
## 62 24 27
## 63 24 42
## 64 25 3
## 65 25 8
## 66 26 17
## 67 26 41
## 68 27 24
## 69 27 32
## 70 27 42
## 71 28 1
## 72 28 2
## 73 28 3
## 74 28 4
## 75 28 6
## 76 28 8
## 77 28 9
## 78 28 10
## 79 28 13
## 80 28 18
## 81 28 21
## 82 28 22
## 83 28 25
## 84 28 27
## 85 28 29
## 86 28 32
## 87 28 34
## 88 28 36
## 89 28 37
## 90 28 38
## 91 31 19
## 92 32 24
## 93 33 21
## 94 34 18
## 95 34 22
## 96 34 28
## 97 34 29
## 98 35 21
## 99 36 1
## 100 36 3
## 101 36 4
## 102 36 8
## 103 36 16
## 104 36 18
## 105 36 26
## 106 36 27
## 107 36 28
## 108 36 34
## 109 37 3
## 110 37 14
## 111 37 38
## 112 38 1
## 113 38 3
## 114 38 8
## 115 38 11
## 116 38 18
## 117 38 22
## 118 38 26
## 119 38 27
## 120 38 28
## 121 38 33
## 122 38 34
## 123 38 36
## 124 38 37
## 125 38 39
## 126 38 43
## 127 42 9
## 128 42 15
## 129 42 24
## 130 42 27
## 131 42 43
## 132 43 5
## 133 43 8
## 134 43 15
## 135 43 18
## 136 43 21
## 137 43 22
## 138 43 23
## 139 43 32
## 140 43 33
## 141 43 34
## 142 43 37
## 143 43 42
Recall from Unit 2 that we introduced the {tidygraph} package for
preparing and summarizing our Twitter network. Tidygraph includes the
full functionality of igraph
in a tidy API giving you
access to almost all of the dplyr
verbs plus a few more,
developed for use with relational data.
Similar to Unit 2, use the tbl_graph()
function to
convert our leader_edges
and leader_nodes
data
frames into a network graph object, by including the following arguments
and supplying the appropriate code:
edges =
expects a data frame, in our case
leader_edges
, containing information about the edges in the
graph. The nodes of each edge must either be in a to
and
from
column, or in the two first columns like the data
frame we provided.
nodes =
expects a data frame, in our case
leader_nodes
, containing information about the nodes in the
graph. If to
and/or from
are characters or
names, like in our data frames, then they will be matched to the column
named according to node_key
in nodes, if it exists, or
matched to the first column in the node list.
directed =
specifies whether the constructed graph
be directed.
# YOUR CODE HERE
leader_graph <- tbl_graph(edges = leader_edges,
nodes = leader_nodes,
directed = TRUE)
leader_graph
## # A tbl_graph: 43 nodes and 143 edges
## #
## # A directed simple graph with 4 components
## #
## # Node Data: 43 × 5 (active)
## id efficacy trust district_site male
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 1 6.06 4 1 0
## 2 2 6.56 5.63 1 0
## 3 3 7.39 4.63 1 0
## 4 4 4.89 4 1 0
## 5 5 6.06 5.75 0 1
## 6 6 7.39 4.38 0 0
## # … with 37 more rows
## #
## # Edge Data: 143 × 2
## from to
## <int> <int>
## 1 3 1
## 2 3 27
## 3 3 28
## # … with 140 more rows
Congrats! You made it to the end of data wrangling section and are ready to start analysis!
In Section 3, we use the {tidygraph} package for retrieving network descriptives and the {ggraph} package to create a network visualization to help illustrate these metrics. Specifically, in this section we will:
Examine Basic Descriptives. We focus primarily on actors and edges in this walkthrough, including whether or not ties were reciprocated and node degree, an important and fairly intuitive measure of centrality.
Make a Sociogram. Finally, we wrap up the explore phases by learning to plot a network and tweak key elements like the size, shape, and position of nodes and edges to better at communicating key findings.
As noted in SNA and Education (Carolan 2014), many analyses of social networks are primarily descriptive and aim to either represent the network’s underlying social structure through data-reduction techniques or to characterize network properties through network measures.
In the analyses described in Chapter 9, descriptives were limited to the mean and standard deviation for: in-degree, out-degree, trust and efficacy measures. The proportion of male leaders was also reported. For section 3a, let’s see if we can reproduce these descriptives.
Recall that degree is the number of ties to and from an ego, or in the case of a “simple graph” like ours, degree is simply the number of people to whom someone is connected. In a directed network, in-degree is the number of connections or ties received, whereas out-degree is the number of connections or ties sent.
The activate()
function from the {tidygraph} package
allows us to treat the nodes in our network object as if they were a
standard data frame to which we can then apply tidyverse functions such
as select()
, filter()
, and
mutate()
.
We can use the mutate()
functions to create new
variables for nodes such as measures of degree, in-degree, and
out-degree using the centrality_degree()
function in the
{tidygraph} package.
Run the following code to add in- and out-degree measures to each of our nodes and examine the output:
leader_measures <- leader_graph |>
activate(nodes) |>
mutate(in_degree = centrality_degree(mode = "in")) |>
mutate(out_degree = centrality_degree(mode = "out"))
leader_measures
## # A tbl_graph: 43 nodes and 143 edges
## #
## # A directed simple graph with 4 components
## #
## # Node Data: 43 × 7 (active)
## id efficacy trust district_site male in_degree out_degree
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1 6.06 4 1 0 5 0
## 2 2 6.56 5.63 1 0 1 0
## 3 3 7.39 4.63 1 0 7 5
## 4 4 4.89 4 1 0 2 4
## 5 5 6.06 5.75 0 1 3 1
## 6 6 7.39 4.38 0 0 1 0
## # … with 37 more rows
## #
## # Edge Data: 143 × 2
## from to
## <int> <int>
## 1 3 1
## 2 3 27
## 3 3 28
## # … with 140 more rows
We now see that, in addition to the previously included attributes
trust
and efficacy
, in-degree and out-degree
measures have been added to the nodes in our network. But what if we
also want to know to total number of “alters” each “ego” is connected
to, i.e. the total number of individuals each school leader are
connected to?
Modify the code below to calculate degree
for each
school leader in our network. Hint:
centrality_degree
is a wrapper for
igraph::degree()
and the mode =
argument can
be found in the corresponding help documentation.
leader_measures <- leader_graph |>
activate(nodes) |>
mutate(in_degree = centrality_degree(mode = "in")) |>
mutate(out_degree = centrality_degree(mode = "out"))
leader_measures
## # A tbl_graph: 43 nodes and 143 edges
## #
## # A directed simple graph with 4 components
## #
## # Node Data: 43 × 7 (active)
## id efficacy trust district_site male in_degree out_degree
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1 6.06 4 1 0 5 0
## 2 2 6.56 5.63 1 0 1 0
## 3 3 7.39 4.63 1 0 7 5
## 4 4 4.89 4 1 0 2 4
## 5 5 6.06 5.75 0 1 3 1
## 6 6 7.39 4.38 0 0 1 0
## # … with 37 more rows
## #
## # Edge Data: 143 × 2
## from to
## <int> <int>
## 1 3 1
## 2 3 27
## 3 3 28
## # … with 140 more rows
We can also use the activate()
function combined with
the as_tibble()
function introduced in our previous unit to
extract our new measures to a separate data frame we’ll call
node_measures
so we can inspect our nodes individually and
later use to calculate some summary stats:
node_measures <- leader_measures |>
activate(nodes) |>
as_tibble()
node_measures
## # A tibble: 43 × 7
## id efficacy trust district_site male in_degree out_degree
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1 6.06 4 1 0 5 0
## 2 2 6.56 5.63 1 0 1 0
## 3 3 7.39 4.63 1 0 7 5
## 4 4 4.89 4 1 0 2 4
## 5 5 6.06 5.75 0 1 3 1
## 6 6 7.39 4.38 0 0 1 0
## 7 7 5.56 3.63 0 1 2 2
## 8 8 7.5 5.63 1 1 8 7
## 9 9 7.67 5.25 0 0 3 2
## 10 10 6.64 4.78 0 0 5 7
## # … with 33 more rows
Now let’s view some basic summary statistics for each of the
variables using the handy summary()
function included in
the R {base} package and the skim()
functions from the
{skimr} package.
summary(node_measures)
## id efficacy trust district_site
## Length:43 Min. :4.610 Min. :3.630 Min. :0.0000
## Class :character 1st Qu.:5.670 1st Qu.:4.130 1st Qu.:0.0000
## Mode :character Median :6.780 Median :4.780 Median :0.0000
## Mean :6.649 Mean :4.783 Mean :0.4186
## 3rd Qu.:7.470 3rd Qu.:5.440 3rd Qu.:1.0000
## Max. :8.500 Max. :5.880 Max. :1.0000
## male in_degree out_degree
## Min. :0.0000 Min. :0.000 Min. : 0.000
## 1st Qu.:0.0000 1st Qu.:2.000 1st Qu.: 1.000
## Median :0.0000 Median :3.000 Median : 2.000
## Mean :0.4419 Mean :3.326 Mean : 3.326
## 3rd Qu.:1.0000 3rd Qu.:5.000 3rd Qu.: 4.000
## Max. :1.0000 Max. :8.000 Max. :20.000
skim(node_measures)
Name | node_measures |
Number of rows | 43 |
Number of columns | 7 |
_______________________ | |
Column type frequency: | |
character | 1 |
numeric | 6 |
________________________ | |
Group variables | None |
Variable type: character
skim_variable | n_missing | complete_rate | min | max | empty | n_unique | whitespace |
---|---|---|---|---|---|---|---|
id | 0 | 1 | 1 | 2 | 0 | 43 | 0 |
Variable type: numeric
skim_variable | n_missing | complete_rate | mean | sd | p0 | p25 | p50 | p75 | p100 | hist |
---|---|---|---|---|---|---|---|---|---|---|
efficacy | 0 | 1 | 6.65 | 1.10 | 4.61 | 5.67 | 6.78 | 7.47 | 8.50 | ▅▅▃▇▃ |
trust | 0 | 1 | 4.78 | 0.71 | 3.63 | 4.13 | 4.78 | 5.44 | 5.88 | ▆▆▅▆▇ |
district_site | 0 | 1 | 0.42 | 0.50 | 0.00 | 0.00 | 0.00 | 1.00 | 1.00 | ▇▁▁▁▆ |
male | 0 | 1 | 0.44 | 0.50 | 0.00 | 0.00 | 0.00 | 1.00 | 1.00 | ▇▁▁▁▆ |
in_degree | 0 | 1 | 3.33 | 2.12 | 0.00 | 2.00 | 3.00 | 5.00 | 8.00 | ▅▇▂▅▂ |
out_degree | 0 | 1 | 3.33 | 4.26 | 0.00 | 1.00 | 2.00 | 4.00 | 20.00 | ▇▁▁▁▁ |
It looks like our summary stats for Year 3 confidential exchanges network and attribute data are pretty close to those reported in Table 9.1 copied from Carolan (2014). For example, our mean total efficacy and trust scores were 6.65 and 4.78 respectively, compared to 6.64 and 4.77 as reported in Chapter 9.
The average in/out-degree for our school leaders data is 3.26,
however, which is a little lower than that reported by Carolyn. Again,
this is likely a function of our removal of self-loops. To test this
theory, we could simply change the diag = FALSE
argument
added to the graph.adjacency()
function above to
TRUE
to include self-loops and then try rerunning all the
code above.
Since we are now working with a standard data frame, we can also
apply {dyplr}
functions like group_by()
and summarise()
to calculate basic summary stats such as counts, mean, and standard
deviation as follows:
node_measures |>
group_by(district_site) |>
summarise(n = n(),
mean = mean(in_degree),
sd = sd(in_degree)
)
## # A tibble: 2 × 4
## district_site n mean sd
## <dbl> <int> <dbl> <dbl>
## 1 0 25 2.36 1.58
## 2 1 18 4.67 2.09
We see that our measures are pretty close, but not an exact match. Again, this is likely due to the self-loops we excluded. For example, our average in-degree for district-level (coded “1”) and school-level (coded “0”) leaders is 4.66 and 2.36 respectively, but reported as 4.72 and 2.64 by Carolan (2014).
Use the code chunk below and additional chunks if needed to try and replicate the school and district level findings for out-degree, trust and efficacy measures.
outdegree <- node_measures|>
group_by(district_site) |>
summarise(n = n(),
mean = mean(out_degree),
sd = sd(out_degree)
)
outdegree
## # A tibble: 2 × 4
## district_site n mean sd
## <dbl> <int> <dbl> <dbl>
## 1 0 25 2.44 3.01
## 2 1 18 4.56 5.41
trust2 <- node_measures|>
group_by(district_site) |>
summarise(n = n(),
mean = mean(trust),
sd = sd(trust)
)
trust2
## # A tibble: 2 × 4
## district_site n mean sd
## <dbl> <int> <dbl> <dbl>
## 1 0 25 4.90 0.719
## 2 1 18 4.62 0.688
efficacy <- node_measures|>
group_by(district_site) |>
summarise(n = n(),
mean = mean(efficacy),
sd = sd(efficacy)
)
efficacy
## # A tibble: 2 × 4
## district_site n mean sd
## <dbl> <int> <dbl> <dbl>
## 1 0 25 7.02 0.953
## 2 1 18 6.13 1.12
How close your result to those reported by Carolyn Table 9.1?
In Chapter 9, Carolan (2014) depicts a directed and dichotomous sociogram of the collaboration network for year 3 is shown in .
Try creating a Year 3 Confidential Exchange Network by modifying the code below and tweaking the included function/arguments or adding new ones for layouts, nodes, and edges to make our plot either more “aesthetically pleasing” or more purposeful in what it’s trying to communicate.
#YOUR CODE HERE
leader_measures |>
ggraph(layout = "fr") +
geom_edge_link(aes(alpha = stat(index))) +
geom_node_point(aes(size = efficacy, alpha = efficacy, color = district_site))
After you are satisfied with your sociogram, answer the following questions?
How is the Year 3 confidential exchange network similar to the collaboration network? How is it different?
Congrats! You made it to the end of the Explore section and are ready to learn a little ab out modeling network selection processes using ERGMs! Before proceeding further, knit your document and check to see if you encounter any errors.
Recall from Carolan (2014) that network models and the questions they are designed to address are categorized into three different analytical emphases:
Relationship-level models that focus on the ties between actors in complete networks;
Models that predict individual actors’ attributes; and,
Actor-level models that emphasize the differences within and among groups of actors within a complete network.
Exponential Random Graph Models fall into category 1 and are relationship-level models that focus on how formation of dyadic ties within a complete relational network can be explained by both structural and attribute variables. ERGMs provide a means to compare whether a network’s observed structural properties occur more frequently than you could expect from chance alone. More specifically, ERGMS provide a way to determine whether observed network properties like reciprocity and assortativity occur by chance as a result of other network properties.
While the technical aspects of estimating ERGMs are complex, their interpretation is pretty straightforward. In this section we are interested exploring how these models can be used to make inferences about the social processes at work in the School Leaders data. For example, in this section we will explore the following questions posed in Chapter 9:
Reciprocity. If one school leader turns to another to discuss something confidential, is the latter likely to reciprocate?
Transitivity. Are school leaders more likely to confide in someone if they both confide in the same school leader?
Attribute Effect. Do a leader’s gender and efficacy score, predict a confidential exchange between two leaders?
As we’ve discovered in this course, network data can come in many different forms — ties can be stored as edgelists or matrices, saved as .csv or excel files, and converted to a variety of network R objects. Attributes for the nodes, ties and dyads in can also various forms and added in various ways to objects in R. For the {ergm} package, however, data will need to be transformed into a object of the class “network” — the format that statnet uses to store and work with network data.
Fortunately, we have already prepared our data for quick conversion
to a network
object and can supply just a couple arguments
to the as.network()
function from the {network} package
used by statnet:
x =
a matrix giving the network structure in
adjacency, incidence, or edgelist form;
vertices
= an optionaldata.frame
containing the vertex attributes. The first column is assigned to the
"vertex.names"
and additional columns are used to set
vertex attributes using their column names.
Similar to the tbl_graph
function from {tidygraph} that
we used above, let’s add our leader_edges
edge-list and our
leader_nodes
data frame as the x =
and
vertices =
arguments in the as.network()
function and assign to a new object called
leader_network
:
leader_network <- as.network(leader_edges,
vertices = leader_nodes)
leader_network
## Network attributes:
## vertices = 43
## directed = TRUE
## hyper = FALSE
## loops = FALSE
## multiple = FALSE
## bipartite = FALSE
## total edges= 143
## missing edges= 0
## non-missing edges= 143
##
## Vertex attribute names:
## district_site efficacy male trust vertex.names
##
## No edge attributes
Note that we could also have supplied our leader_matrix
as the first argument, but using and edgelist vastly simplifies the
process of adding node attributes.
Let’s also check the be sure that our leader_network
is
indeed a network
object by using the class()
function to identify object type:
class(leader_network)
## [1] "network"
So far so good! Let’s start building our first ERGM model!
We’ll begin by running a simple model both to demonstrate the most commonly used functions for ERGMs and also to replicate the approach in Chapter 9 of Carolan (2014).
The syntax for specifying a model in the ergm
package
follows R’s standard formula convention:
my_network ~ ergm_term_1 + ergm_term_2 + ergm_term_3
and so
forth.
This syntax is used for both
the summary
and ergm
functions.
The summary
function simply returns the numerical
values of the network statistics in the model.
The ergm
function estimates the model with those
statistics.
As noted by the Statnet Development Team:
It is good practice to run a
summmary
command on any model before fitting it withergm
. This is the ERGM equivalent of performing some descriptive analysis on your covariates. This can help you make sure you understand what the term represents, and it can help to flag potential problems that will lead to poor modeling results.
Let’s start with with a simple model similar to the P1
model in Table 9.4. This model contains the required ergm-term
edges
that represents the total number of
edges in the network, and the ergm-term
mutual
that examines the tendency for ties
to be reciprocated, i.e. “mutuality”.
summary(leader_network ~ edges + mutual)
## edges mutual
## 143 36
We see from our summary that our leader_network
consists
of 143 edges and 36 reciprocated dyads.
Since the ergm()
function uses a stochastic MCMC-based
estimation algorithm, use the set.seed()
function and set
the value to 589 (note this could be any number besides our course
number) so we produce the same results each time.
Now let’s estimate our model, save the results as
ergm_mod_1
, and use the summary()
function
again to take a look at our estimates. Also,
set.seed(589)
ergm_mod_1 <-ergm(leader_network ~ edges + mutual)
summary(ergm_mod_1)
## Call:
## ergm(formula = leader_network ~ edges + mutual)
##
## Monte Carlo Maximum Likelihood Results:
##
## Estimate Std. Error MCMC % z value Pr(>|z|)
## edges -3.1101 0.1283 0 -24.23 <1e-04 ***
## mutual 3.1246 0.3003 0 10.40 <1e-04 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Null Deviance: 2503.6 on 1806 degrees of freedom
## Residual Deviance: 891.8 on 1804 degrees of freedom
##
## AIC: 895.8 BIC: 906.8 (Smaller is better. MC Std. Err. = 0.7447)
Since ERGMs predict the presence of a network tie, with estimates indicating the importance of each to the presence of a tie, estimated coefficients can be explained in terms similar to logistic regression. That is, positive significant coefficients indicate that the corresponding parameters in the observed network (e.g. reciprocated ties between school leaders), controlling for all other parameters in the model, occur more than would be expected by chance, thus increasing the likelihood that a tie will occur, and vice-versa for negative coefficients.
After several iterations, we see that our model finally converges and
suggest that the estimates for our edges
and
mutual
terms are statistically significant. The negative
estimate for the edge
paramter, as noted by Carolan (2014), implies that the probability of
a confidential exchange tie in year 3 is relatively low. The reciprocity
parameter, on the other hand, is 3.11 in our model, and indicates a
strong tendency for confidential-exchange ties to be reciprocated.
Now let’s add an ergm-term for transitivity. Modeling transitivity, or the “friend of a friend” phenomenon in social networks, is both computationally intensive because of the all the possible “triangles” in a directed network, and also very prone to model degeneration, i.e. when models that fail to converge by reaching a value expected by the parameters included in the model.
Ergm-terms for transitivity such as triangles
and
transitive
in particular are prone to model degeneration.
Fortunately, the {ergm} package includes a “more robust way of modeling
triangles: the geometrically-weighed edgewise shared partner term
(GWESP).”
Let’s add the gwesp
term to our model with the suggested
defaults from the Statnet Tutorial section on What
it looks like when a model fails and take a look at the summary
first:
summary(leader_network ~ edges +
mutual +
transitive +
gwesp(0.25, fixed=T))
## edges mutual transitive gwesp.fixed.0.25
## 143.0000 36.0000 202.0000 116.2971
As you can see, we have 202 transitive triad types.
Now lets run our model including transitivity and take a look at our
estimates. Note that this make take a couple minutes to run. If you’d
like to watch the number of iterations of the model set
message = TRUE
.
ergm_mod_2 <-ergm(leader_network ~ edges +
mutual +
gwesp(0.25, fixed=T))
summary(ergm_mod_2)
## Call:
## ergm(formula = leader_network ~ edges + mutual + gwesp(0.25,
## fixed = T))
##
## Monte Carlo Maximum Likelihood Results:
##
## Estimate Std. Error MCMC % z value Pr(>|z|)
## edges -3.9745 0.1522 0 -26.117 <1e-04 ***
## mutual 2.2989 0.3171 0 7.250 <1e-04 ***
## gwesp.fixed.0.25 1.0713 0.1305 0 8.207 <1e-04 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Null Deviance: 2503.6 on 1806 degrees of freedom
## Residual Deviance: 805.7 on 1803 degrees of freedom
##
## AIC: 811.7 BIC: 828.2 (Smaller is better. MC Std. Err. = 0.487)
Contrary to the analysis in Chapter 9, our model so far suggest that there is a tendency toward transitivity, that is a confidential exchange is likely to occur between two people who both have a confidential exchange with the same individual.
At this point, however, we have only focused on structural mechanisms inherent to networks themselves, or “global features” of the graph independent of actors, and have not looked at individual attributes among actors that may be shaping out network.
As noted by Carolan (2014), ERGMs have advanced to the point where actor attributes, or actor-level covariates, can now be incorporated into model estimation, such as individuals’ demographic (e.g., gender) or behavioral (e.g., efficacy) characteristics. These two attributes examined in Chapter 9 with leadership efficacy measured by survey items based on the Principal Efficacy Scale.
The attributes were included as both sender and receiver effects, which again can be very computationally intensive and thus timely to run, while sometimes prone to model degeneracy without some model fine tuning.
To simplify this approach, I’ve include these attributes using the
nodefactor()
and nocov()
ergm-terms
respectively which allow us to test whether those who are Male or have
higher efficacy scores are more likely to either send or receive a
confidential exchange.
ergm_3 <- ergm(leader_network ~ edges +
mutual +
gwesp(0.25, fixed=T) +
nodefactor('male') +
nodecov('efficacy')
)
summary(ergm_3)
## Call:
## ergm(formula = leader_network ~ edges + mutual + gwesp(0.25,
## fixed = T) + nodefactor("male") + nodecov("efficacy"))
##
## Monte Carlo Maximum Likelihood Results:
##
## Estimate Std. Error MCMC % z value Pr(>|z|)
## edges -4.35415 0.42693 0 -10.199 <1e-04 ***
## mutual 2.29853 0.31875 0 7.211 <1e-04 ***
## gwesp.fixed.0.25 1.05200 0.13281 0 7.921 <1e-04 ***
## nodefactor.male.1 0.10622 0.06547 0 1.622 0.105
## nodecov.efficacy 0.02230 0.02967 0 0.752 0.452
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Null Deviance: 2503.6 on 1806 degrees of freedom
## Residual Deviance: 802.5 on 1801 degrees of freedom
##
## AIC: 812.5 BIC: 840 (Smaller is better. MC Std. Err. = 0.4733)
Although there appears to be a very slight, though not statistically significant tendency towards male leaders being more likely to send or receive ties, the primary drivers of network formation appear to be structural features of the network including reciprocity and transitivity.
Sadly, we were not able to replicate the findings from Carolan (2014), but we also did not replicate their model exactly, nor did we fine tune our model parameters. Regardless, this provided a practical exercise for demonstrating ERGMs.
Before moving on to checking the goodness of fit for our final model,
try testing to see if shared characteristics among school leaders might
help explain tie formation. To test for homophily among gender or
location, the ergm()
function includes a
nodematch()
argument similar to nodefactor()
used above that could be used to answer the following question:
#YOUR CODE HERE
ergm_4 <- ergm(leader_network ~ edges +
mutual +
gwesp(0.25, fixed=T) +
nodematch('male') +
nodecov('efficacy')
)
summary(ergm_4)
## Call:
## ergm(formula = leader_network ~ edges + mutual + gwesp(0.25,
## fixed = T) + nodematch("male") + nodecov("efficacy"))
##
## Monte Carlo Maximum Likelihood Results:
##
## Estimate Std. Error MCMC % z value Pr(>|z|)
## edges -4.21148 0.42044 0 -10.017 <1e-04 ***
## mutual 2.29888 0.32391 0 7.097 <1e-04 ***
## gwesp.fixed.0.25 1.06715 0.12779 0 8.351 <1e-04 ***
## nodematch.male -0.14499 0.16827 0 -0.862 0.389
## nodecov.efficacy 0.02326 0.02966 0 0.784 0.433
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Null Deviance: 2504 on 1806 degrees of freedom
## Residual Deviance: 805 on 1801 degrees of freedom
##
## AIC: 815 BIC: 842.5 (Smaller is better. MC Std. Err. = 0.4946)
Once you’ve checked the summary of your model estimates, write your interpretation of the results below:
We’ve only scratched the surface for the number of network and node attributes properties that can be tested using ERGMs. Take a look at a table of common ergm-terms or type “ergm-terms” into the help menu of the files pane for some additional examples.
One test of whether an ergm model is a “good fit” for the data is
“how well it reproduces the observed global network properties that
are not in the model.” This can be accomplished by choosing a
network statistic that is not in the model, and comparing the value of
this statistic observed in the original network to the distribution of
values we get in simulated networks from our model, using the
gof()
function to test the goodness-of-fit.
The gof()
function is a bit different than the
summary()
and ergm()
functions, in that it
only takes 3 ergm-terms as arguments: degree, esp (edgwise share
partners), and distance (geodesic distances). Each of these terms
captures an aggregate network distribution, at either the node level
(degree), the edge level (esp), or the dyad level (distance).
Let’s go ahead and run the gof()
function on our
ergm_3
model and plot the results:
ergm_3_gof <- gof(ergm_3)
plot(ergm_3_gof)
Overall, the model appears to fit reasonably well in that black line in our charts (the actual observed network measures) closely follows the aggregate measures generated from the simulations, or permutations of our network.
One final check on our model is to examine the Monte-Carlo Markov Chain (MCMC) diagnostics to make sure our model is not heading of in the wrong direction and likely to never produce a network similar to what was observed, which is referred to as “model degeneracy.” As the statnet authors note:
When a model is not a good representation of the observed network, the simulated networks produced in the MCMC chains may be far enough away from the observed network that the estimation process is affected. In the worst case scenario, the simulated networks will be so different that the algorithm fails altogether.
Fortunately, our models did not fail to converge, the AIC and BIC indicators seemed to improve with each term added to the model, and the goodness-of-fit seemed to somewhat mirror the global features of our network.
Let run the mcmc.diagnostics()
function on our final
model anyways and check the results:
mcmc.diagnostics(ergm_3)
## Sample statistics summary:
##
## Iterations = 7393280:31387648
## Thinning interval = 4096
## Number of chains = 1
## Sample size per chain = 5859
##
## 1. Empirical mean and standard deviation for each variable,
## plus standard error of the mean:
##
## Mean SD Naive SE Time-series SE
## edges -4.591 51.45 0.6721 2.6523
## mutual -1.464 16.83 0.2199 0.8491
## gwesp.fixed.0.25 -4.969 63.58 0.8306 3.2645
## nodefactor.male.1 -4.895 59.53 0.7778 3.1039
## nodecov.efficacy -62.654 696.91 9.1047 36.0250
##
## 2. Quantiles for each variable:
##
## 2.5% 25% 50% 75% 97.5%
## edges -93.0 -45.00 -4.000 34.00 93.00
## mutual -30.0 -15.00 -2.000 11.00 31.55
## gwesp.fixed.0.25 -108.3 -56.99 -7.051 41.82 119.66
## nodefactor.male.1 -107.5 -52.00 -5.000 39.00 107.55
## nodecov.efficacy -1259.6 -616.21 -55.990 454.95 1263.27
##
##
## Are sample statistics significantly different from observed?
## edges mutual gwesp.fixed.0.25 nodefactor.male.1
## diff. -4.59122717 -1.46373101 -4.9694724 -4.8953746
## test stat. -1.73105929 -1.72390925 -1.5222868 -1.5771922
## P-val. 0.08344119 0.08472423 0.1279372 0.1147513
## nodecov.efficacy Overall (Chi^2)
## diff. -62.65407919 NA
## test stat. -1.73918395 4.130851e+01
## P-val. 0.08200241 1.006831e-07
##
## Sample statistics cross-correlations:
## edges mutual gwesp.fixed.0.25 nodefactor.male.1
## edges 1.0000000 0.9790145 0.9913763 0.9654745
## mutual 0.9790145 1.0000000 0.9813615 0.9488636
## gwesp.fixed.0.25 0.9913763 0.9813615 1.0000000 0.9623458
## nodefactor.male.1 0.9654745 0.9488636 0.9623458 1.0000000
## nodecov.efficacy 0.9988166 0.9781341 0.9906310 0.9634763
## nodecov.efficacy
## edges 0.9988166
## mutual 0.9781341
## gwesp.fixed.0.25 0.9906310
## nodefactor.male.1 0.9634763
## nodecov.efficacy 1.0000000
##
## Sample statistics auto-correlation:
## Chain 1
## edges mutual gwesp.fixed.0.25 nodefactor.male.1
## Lag 0 1.0000000 1.0000000 1.0000000 1.0000000
## Lag 4096 0.8563656 0.8611438 0.8607252 0.8547248
## Lag 8192 0.7507988 0.7552559 0.7531766 0.7492259
## Lag 12288 0.6641857 0.6662636 0.6647575 0.6661954
## Lag 16384 0.5869818 0.5872707 0.5853704 0.5909378
## Lag 20480 0.5156311 0.5189978 0.5140322 0.5225789
## nodecov.efficacy
## Lag 0 1.0000000
## Lag 4096 0.8572204
## Lag 8192 0.7515372
## Lag 12288 0.6653919
## Lag 16384 0.5886474
## Lag 20480 0.5177033
##
## Sample statistics burn-in diagnostic (Geweke):
## Chain 1
##
## Fraction in 1st window = 0.1
## Fraction in 2nd window = 0.5
##
## edges mutual gwesp.fixed.0.25 nodefactor.male.1
## 0.020048 -0.045774 -0.026767 0.049916
## nodecov.efficacy
## -0.008712
##
## Individual P-values (lower = worse):
## edges mutual gwesp.fixed.0.25 nodefactor.male.1
## 0.9840052 0.9634904 0.9786455 0.9601895
## nodecov.efficacy
## 0.9930490
## Joint P-value (lower = worse): 0.4086241 .
##
## MCMC diagnostics shown here are from the last round of simulation, prior to computation of final parameter estimates. Because the final estimates are refinements of those used for this simulation run, these diagnostics may understate model performance. To directly assess the performance of the final model on in-model statistics, please use the GOF command: gof(ergmFitObject, GOF=~model).
One simple heuristic for interpreting mcmc.diagnostics()
results is too look for “balance.” The charts with all the squiggles, to
use a non-technical term, should have a scale that ideally is centered
on 0 and have roughly straight line running through it, while the chart
that looks like a simple line graph should also be ideally centered at 0
and roughly normally distributed on both ends. Overall, the diagnostics
indicate there is room for improvement but nothing is wildly off and
hence our model did not fail to converge.
Use the code chunk below to check the model fit and diagnostics for the model the you created in 4b. Estimate the ERGM Model:
ergm_4_gof <- gof(ergm_4)
plot(ergm_4_gof)
mcmc.diagnostics(ergm_4)
## Sample statistics summary:
##
## Iterations = 1753088:50331648
## Thinning interval = 16384
## Number of chains = 1
## Sample size per chain = 2966
##
## 1. Empirical mean and standard deviation for each variable,
## plus standard error of the mean:
##
## Mean SD Naive SE Time-series SE
## edges -5.047 55.74 1.0235 2.2935
## mutual -1.744 18.15 0.3332 0.7609
## gwesp.fixed.0.25 -6.052 68.56 1.2588 2.8348
## nodematch.male -1.883 26.22 0.4814 1.0466
## nodecov.efficacy -68.128 754.50 13.8540 31.0979
##
## 2. Quantiles for each variable:
##
## 2.5% 25% 50% 75% 97.5%
## edges -94 -52.00 -8.00 36.00 105.9
## mutual -31 -17.00 -3.00 11.00 34.0
## gwesp.fixed.0.25 -109 -64.42 -11.24 43.19 133.7
## nodematch.male -43 -24.00 -3.00 17.00 52.0
## nodecov.efficacy -1275 -704.80 -102.95 489.79 1427.3
##
##
## Are sample statistics significantly different from observed?
## edges mutual gwesp.fixed.0.25 nodematch.male
## diff. -5.04720162 -1.74376264 -6.0520442 -1.88267026
## test stat. -2.20065937 -2.29176493 -2.1349193 -1.79879464
## P-val. 0.02776015 0.02191921 0.0327676 0.07205117
## nodecov.efficacy Overall (Chi^2)
## diff. -68.1277680 NA
## test stat. -2.1907498 2.462633e+01
## P-val. 0.0284699 1.781574e-04
##
## Sample statistics cross-correlations:
## edges mutual gwesp.fixed.0.25 nodematch.male
## edges 1.0000000 0.9821387 0.9919954 0.9738180
## mutual 0.9821387 1.0000000 0.9843173 0.9544524
## gwesp.fixed.0.25 0.9919954 0.9843173 1.0000000 0.9664271
## nodematch.male 0.9738180 0.9544524 0.9664271 1.0000000
## nodecov.efficacy 0.9989921 0.9816024 0.9914252 0.9732447
## nodecov.efficacy
## edges 0.9989921
## mutual 0.9816024
## gwesp.fixed.0.25 0.9914252
## nodematch.male 0.9732447
## nodecov.efficacy 1.0000000
##
## Sample statistics auto-correlation:
## Chain 1
## edges mutual gwesp.fixed.0.25 nodematch.male nodecov.efficacy
## Lag 0 1.0000000 1.0000000 1.0000000 1.0000000 1.0000000
## Lag 16384 0.6459246 0.6473194 0.6439058 0.6184790 0.6470462
## Lag 32768 0.4394995 0.4507851 0.4418863 0.4156574 0.4407662
## Lag 49152 0.2933342 0.3023061 0.2906277 0.2791341 0.2936363
## Lag 65536 0.1900030 0.1975078 0.1918624 0.1806380 0.1905881
## Lag 81920 0.1198984 0.1204661 0.1183082 0.1140627 0.1202699
##
## Sample statistics burn-in diagnostic (Geweke):
## Chain 1
##
## Fraction in 1st window = 0.1
## Fraction in 2nd window = 0.5
##
## edges mutual gwesp.fixed.0.25 nodematch.male
## 0.9557 0.8491 0.9365 1.1736
## nodecov.efficacy
## 0.9592
##
## Individual P-values (lower = worse):
## edges mutual gwesp.fixed.0.25 nodematch.male
## 0.3392118 0.3958112 0.3489923 0.2405570
## nodecov.efficacy
## 0.3374384
## Joint P-value (lower = worse): 0.3277513 .
##
## MCMC diagnostics shown here are from the last round of simulation, prior to computation of final parameter estimates. Because the final estimates are refinements of those used for this simulation run, these diagnostics may understate model performance. To directly assess the performance of the final model on in-model statistics, please use the GOF command: gof(ergmFitObject, GOF=~model).
For your final Your Turn, your goal is to distill our analysis from above into a simple “data product” designed to illustrate key findings about changes in the collaboration network over time. For the purposes of this task, imagine that your audience consists of the developers and facilitators of the DLT MOOC-Eds who have limited background in SNA and adapt the following steps accordingly:
Select. Select your ERGM analysis from above, or a new analysis if so motivated, that you think would be interesting or relevant for the target audience and that helps answer our research questions.
Polish. Create and polish a data visualization and/or data table to communicate your selected findings.
Narrate. Write a brief narrative (2-3 paragraphs) to accompany your visualization and/or table that includes the following:
The question or questions guiding the analysis;
The conclusions you’ve reached based on our findings;
How your audience might use this information;
How you might revisit or improve upon this analysis in the future.
Use the code chunk below create a polished table and/or visualization(s) and write a brief narrative in the space that follows.
m1b <- as.formula( leader_network ~ # The network
mutual('male', diff = TRUE) + # How many ties are reciprocated?
edges) # Edges term
fit1b <- ergm(m1b)
#summarize model
summary(fit1b)
## Call:
## ergm(formula = m1b)
##
## Monte Carlo Maximum Likelihood Results:
##
## Estimate Std. Error MCMC % z value Pr(>|z|)
## mutual.same.male.0 1.5008 0.4711 0 3.186 0.00144 **
## mutual.same.male.1 2.9323 0.3695 0 7.935 < 1e-04 ***
## edges -2.6898 0.1065 0 -25.257 < 1e-04 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Null Deviance: 2503.6 on 1806 degrees of freedom
## Residual Deviance: 952.1 on 1803 degrees of freedom
##
## AIC: 958.1 BIC: 974.6 (Smaller is better. MC Std. Err. = 0.4556)
#print list and interpret P value
screenreg(list(fit1b))
##
## ===============================
## Model 1
## -------------------------------
## mutual.same.male.0 1.50 **
## (0.47)
## mutual.same.male.1 2.93 ***
## (0.37)
## edges -2.69 ***
## (0.11)
## -------------------------------
## AIC 958.08
## BIC 974.58
## Log Likelihood -476.04
## ===============================
## *** p < 0.001; ** p < 0.01; * p < 0.05
#plot covariate of Male at 95% Confidence
plotreg(list(fit1b),
theme = theme_dark(),
custom.model.names = "Covariates",
custom.coef.names = c("Male", "Edge", "Female"),
ggtitle = "Do males have more reciprocated ties than females?")
I decided to explore the ERGM package and learn how to plot confidence intervals. I am still getting the hang of the interpretations and with what fuunctions to use. I will show more of that in the independent study.
My question wondered whether males have more reciprocated ties than females. This would mean our hypothesis is that gender has no effect on mutually reciprocating ties. In the summary output, we can see that males have a higher effect that is positive and significant. Moreover, males have a slightly higher propensity to engage in mutual behavior. Overall, the effects are quite similar for the two groups. In the list view P < .001 and we have strong evidence to reject the null hypothesis.
This information can be used to increase mutual reciprocation between all genders within a district. Being able to receive and send help when needed is important for a positive work environment. Additionally, understanding where the holes are central office can offer support and relationship building workshops. Reciprocity further allows important tasks to get done in a timely manner if you have help getting beyond your Zone of Proximinal development.
Congratulations - you’ve completed the Unit 3 case study! To share your work, click the drop down arrow next to the ball of yarn that says “Knit” at the top of this markdown file, then select “Knit top HTML”. Assuming your code contains no errors, this will create a web page in your Files pane that serves as a record of your work.
Once your file has been knitted, you can publish this file online using RPubs, or share the HTML file through another means.