1. PREPARE

1a. Data Sources

The data for this analysis is Daly’s Network of School District Leaders. Specifically, I’m using two data sets describing the school leaders and their collaboration:

  • The adjacency matrix reports on “collaboration” 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).

  • 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).

A Social Network Perspective in School Leader Accountability

Daly, A. J., & Finnigan, K. S. (2011). The ebb and flow of social network ties between district leaders under high-stakes accountability. American Educational Research Journal, 48(1), 39-79.

Abstract

Recent studies suggests the importance of school district offices in supporting reform. These works provide strategies for building relations between central offices and sites in order to improve change efforts. However, what is frequently overlooked is that organizational reform efforts are socially constructed. Therefore, examining the underlying reform-related social networks may provide insight into how relational structures support or constrain efforts at reform. This longitudinal case study draws upon social network analysis and interviews to examine the reform-related knowledge, advice, and innovation network structures of central office and site leaders in a district facing sanction for underperformance and engaging a districtwide reform. Findings indicate that over time, the networks increased the number of superficial interactions, and more frequent exchanges remained unchanged, resulting in a centralized network structure.

1b. Identify a Question

This project focuses on the data generated by year 3 of the study:

How does gender and trust impact school leader affinity for building strong collaboration networks?

Specifically, my goal is to compare engagement between participants of the final year iteration as an indicator of the correlation of gender and/or trust scores on peer collaboration.

1c. Load Libraries

library(statnet)
library(tidyverse)
library(readxl)
library(igraph)
library(tidygraph)
library(ggraph)
library(skimr)
library(janitor)
library(kableExtra)

2. WRANGLE

For data wrangling, the focus is on working with network data stored as an adjacency matrix. This section will include the following steps:

  1. Import Data. The {readxl} package from the tidyverse will be employed to read in a matrix and node attributes.

  2. Dichotomize a Matrix. Edge values will be recoded to 1s and 0s, transforming the valued matrix to a binary matrix.

  3. Create Network Graph. Lastly, the matrix will be converted to an edge-list to store both the edges and node attributes as a network igraph object in preparation for analysis.

2a. Import Data

Node Attributes

The first file contains all the nodes or actors (i.e., participants who posted to the discussion forum) as well as some of their attributes such as gender and years of experience in education. These attribute variables are typically included in a rectangular array, or dataframe, that mimics the actor-by-attribute that is the dominant convention in social science, i.e. rows represent cases, columns represent variables, and cells consist of values on those variables.

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:

id efficacy trust district_site male
1 6.06 4.00 1 0
2 6.56 5.63 1 0
3 7.39 4.63 1 0
4 4.89 4.00 1 0
5 6.06 5.75 0 1
6 7.39 4.38 0 0
7 5.56 3.63 0 1
8 7.50 5.63 1 1
9 7.67 5.25 0 0
10 6.64 4.78 0 0
11 5.06 5.88 0 1
12 7.44 3.75 0 1
13 8.28 5.13 0 0
14 5.67 5.50 0 0
15 6.72 5.63 0 1
16 4.61 3.75 1 0
17 7.17 5.13 0 0
18 5.00 4.13 1 1
19 7.89 3.88 0 1
20 7.17 5.75 0 1
21 7.44 5.38 0 1
22 4.94 3.75 1 0
23 8.50 5.50 0 0
24 5.22 5.13 1 0
25 6.94 4.63 1 0
26 7.00 4.88 0 0
27 5.06 4.50 1 0
28 7.61 4.50 1 1
29 5.67 4.13 1 1
30 5.50 4.88 0 1
31 6.78 4.25 0 1
32 5.88 4.13 1 0
33 5.89 5.13 0 0
34 5.12 4.25 1 1
35 7.44 3.88 0 1
36 7.72 5.75 1 0
37 6.64 4.78 1 1
38 7.56 5.75 1 1
39 8.00 5.63 0 0
40 7.33 5.38 0 0
41 8.17 4.13 0 0
42 8.06 5.25 0 1
43 6.72 3.88 0 0

Note that the ID column is specified as “text” while the remaining columns are “numeric.” The first column indicates the “names” for each school leader and this column will be used to assign names to the columns and rows for the adjacency matrix.

Below are the additional node variables:

  • efficacy = Score 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 = Composite measurement of how trusting a school leader is of multiple individuals (students, parents, colleagues, and principals). Trust is rated on a 7-point Likert scale ranging from 1 (Strongly disagree) to 7 (Strongly agree).
  • district_site = Identifier of institution level, 1 for district-level and 0 for school-level.
  • male = 1 for yes, 0 for no (female).

Edge Attributes

Study participant 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 this case, the tie is a directed and valued “arc” where the value indicates the strength of the relationship.

## New names:
## * `` -> ...1
## * `` -> ...2
## * `` -> ...3
## * `` -> ...4
## * `` -> ...5
## * ...
…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 …29 …30 …31 …32 …33 …34 …35 …36 …37 …38 …39 …40 …41 …42 …43
0 2 4 3 1 1 1 3 1 1 1 1 1 1 1 2 1 2 1 1 1 2 1 2 1 1 2 4 1 1 1 2 1 1 1 4 1 3 1 1 1 1 1
0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 2 0 1 0 0 0 2 0 0 1 0 0 4 1 0 0 1 1 1 0 0 0 0 0 0 0 0 0
4 1 0 1 2 2 2 3 2 2 2 2 2 2 2 3 1 2 2 2 2 1 2 2 2 2 4 3 1 2 1 2 2 2 3 3 3 3 3 2 2 2 2
3 2 0 0 0 0 0 1 0 0 1 0 0 0 0 4 0 3 0 0 0 3 0 1 2 0 2 4 0 0 0 2 0 4 0 4 0 2 0 0 0 0 0
1 0 2 0 0 1 1 2 3 0 1 1 2 1 1 0 1 1 1 1 2 1 2 2 1 2 2 2 0 2 1 0 1 1 0 1 2 2 2 1 1 1 2
3 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0
2 1 1 1 2 1 4 2 1 4 1 1 1 2 1 2 2 1 2 4 1 1 1 2 1 2 1 1 1 1 2 1 1 1 1 1 1 2 1 2 2 1 2
3 2 4 1 3 3 2 0 3 3 2 3 3 2 3 4 1 4 3 2 3 3 3 2 4 4 4 4 2 2 1 3 2 2 1 4 3 4 2 4 1 2 2
2 0 2 0 4 2 0 3 0 1 2 2 1 4 3 1 2 2 1 1 2 1 2 1 0 0 2 1 0 1 1 1 2 1 0 1 1 2 1 0 0 3 2
2 1 3 2 2 1 4 4 3 0 3 2 2 3 2 2 2 3 3 4 3 1 2 2 1 3 2 2 2 3 2 1 2 3 1 1 2 4 0 3 3 1 3
3 1 2 2 4 3 3 3 3 3 0 4 3 3 4 3 3 2 4 2 4 3 4 2 2 1 2 2 1 1 1 2 3 3 3 1 2 3 1 1 4 2 4
1 0 2 0 1 1 1 3 2 1 1 0 0 0 1 0 0 1 0 0 1 0 1 1 0 0 1 1 0 0 0 0 2 0 0 0 2 1 1 0 0 0 0
0 1 4 0 3 1 0 3 1 0 1 0 0 1 1 0 0 1 0 0 0 1 4 1 0 1 3 0 1 0 0 1 0 1 4 0 3 1 1 1 0 3 1
2 0 3 1 2 2 3 2 3 3 2 2 1 4 3 1 3 1 3 3 2 1 2 2 0 3 2 2 2 3 3 0 2 2 0 2 4 3 2 3 3 1 2
1 1 2 2 3 0 2 2 2 1 2 3 2 3 0 1 2 1 2 2 4 1 2 4 1 2 4 1 1 2 4 2 3 2 2 1 3 2 3 2 2 4 4
0 1 1 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
1 1 1 1 2 1 2 1 1 2 1 1 1 2 1 1 4 1 2 2 1 1 1 1 1 4 1 2 1 2 2 1 1 1 1 1 2 1 1 2 4 0 2
4 2 2 2 2 2 2 3 2 3 2 2 1 1 2 2 1 0 2 2 1 4 1 1 2 3 2 4 4 1 1 3 1 4 1 4 3 4 1 2 2 1 2
0 0 2 0 0 0 0 2 0 0 0 0 0 0 0 0 0 2 4 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 2 2 0 0 0 2 0
3 1 2 1 3 1 4 2 2 4 2 1 1 2 1 1 1 3 3 0 1 2 1 2 1 2 1 1 1 2 3 1 1 2 1 1 2 2 1 2 1 1 2
1 0 2 0 2 1 1 2 2 0 3 2 2 3 3 1 1 0 1 1 0 0 2 0 2 1 0 1 0 1 1 0 3 1 4 0 0 1 2 1 1 2 2
1 2 1 1 1 1 1 3 1 1 1 1 1 1 1 4 1 3 1 1 1 0 1 1 1 1 2 4 3 1 1 2 1 3 1 1 1 3 1 1 1 1 1
3 0 2 2 2 2 2 3 2 0 3 2 2 0 2 0 0 1 0 0 2 0 0 0 0 0 1 1 1 0 0 1 0 0 0 0 1 0 0 0 0 2 2
2 0 3 0 1 1 1 4 1 2 2 1 1 1 3 1 1 1 1 1 1 2 1 0 2 2 4 1 0 2 1 4 2 1 1 0 1 2 1 1 1 4 1
1 2 3 2 0 2 0 4 0 0 0 1 1 0 1 1 0 3 0 0 2 2 0 3 4 0 0 1 2 0 0 4 3 3 2 2 1 1 1 0 0 3 1
1 0 2 0 0 0 0 2 0 1 0 0 0 1 0 1 4 2 0 0 0 1 0 1 0 0 1 1 1 1 0 0 0 2 0 1 1 2 0 0 4 0 0
0 0 4 0 1 0 1 3 1 0 1 1 1 0 3 0 0 1 0 0 0 0 2 4 4 1 0 0 0 3 0 4 0 0 1 2 0 2 1 0 0 4 0
4 4 4 4 4 3 3 4 4 3 3 3 4 3 3 4 3 4 3 3 4 4 3 3 3 4 4 4 4 3 3 4 3 4 3 4 4 4 3 3 3 3 3
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 4 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0
1 1 2 1 2 1 1 2 1 1 1 1 1 1 1 1 1 1 2 1 1 2 1 2 1 1 2 1 1 4 1 1 1 1 1 1 1 2 1 1 1 1 1
0 0 0 0 2 0 2 0 0 0 0 0 0 0 2 0 0 0 4 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0
2 2 2 2 0 1 0 2 1 0 1 1 0 0 2 1 0 2 0 0 0 1 2 3 4 0 4 1 1 0 0 0 1 1 0 2 1 1 0 0 0 1 1
1 2 2 1 2 3 1 3 3 1 2 3 2 2 2 1 1 2 2 1 4 1 2 2 2 1 2 2 2 1 1 2 0 2 1 1 2 3 2 1 1 1 3
4 3 4 4 2 2 1 4 2 2 2 2 2 1 2 3 1 4 1 1 1 4 1 3 2 2 4 4 4 1 1 3 2 0 3 4 4 4 1 2 2 2 2
1 1 2 2 1 0 1 2 0 0 1 0 4 0 0 0 0 2 0 0 2 2 1 0 0 0 1 2 2 0 1 1 0 2 0 0 2 0 0 0 0 2 1
4 2 4 4 1 0 1 4 1 0 1 0 0 0 0 4 0 4 1 0 1 4 0 1 2 2 4 4 2 0 0 2 1 4 0 0 4 4 0 0 0 1 0
1 1 4 1 3 3 3 3 3 3 3 3 3 4 3 1 3 3 3 3 3 2 3 2 3 3 3 3 1 0 3 2 3 3 3 2 0 4 3 3 3 3 3
4 2 4 2 3 3 2 4 3 3 3 3 3 3 3 2 2 4 3 3 3 3 3 2 2 4 4 4 2 3 2 2 4 4 2 4 4 0 4 3 2 2 3
1 0 2 0 1 2 0 2 1 0 1 1 1 0 1 1 0 1 1 0 1 1 1 1 1 0 1 1 0 0 1 0 1 0 0 0 1 2 0 0 0 2 1
0 0 1 0 1 1 2 2 0 0 0 0 1 2 0 0 2 1 2 2 2 0 0 2 1 0 2 1 1 2 2 0 0 1 0 0 2 2 0 4 2 0 2
1 0 2 4 2 2 1 2 1 2 4 1 1 2 1 1 4 2 2 2 1 2 1 1 2 4 2 2 1 2 2 1 1 1 1 1 2 2 1 2 4 2 1
2 0 2 0 1 1 0 2 4 0 2 2 2 0 4 0 0 1 2 0 2 2 0 4 3 0 4 2 0 0 2 0 0 2 2 0 0 3 2 0 2 4 4
1 1 1 1 2 1 2 3 2 2 2 2 1 2 3 1 2 2 2 2 3 2 3 2 1 2 1 2 2 1 2 3 3 3 1 1 3 1 3 1 1 4 0

2b. Create Network Object

In this case, we are working with “valued realtions” that need to be “dichotomized” in our network. This simply entails converting the valued matrix to a binary matrix with cells containing just 1s or 0s. However, before we can do that, the nodes and edges must be transformed into a matrix.

Convert to Matrix Object

The imported data will first be converted to a matrix object recognized by R using the as.matrix() function. I can then check the data format of the leader_matrix using the class() function:

leader_matrix <- leader_matrix |>
  as.matrix()

class(leader_matrix)
## [1] "matrix" "array"

Dichotimize Matrix Object

To dichotomize the matrix, the following code will “assign” 0s to all values in the matrix that are less than or equal to 2, and 1’s to all values that are greater than or equal to 3:

leader_matrix[leader_matrix <= 2] <- 0

leader_matrix[leader_matrix >= 3] <- 1

Add Row and Column Names

Before converting to an edge-list, the names of the nodes will need to be added to the columns and rows of the matrix. These are stored in the ID column of the leader_node data frame. The $ operator is used to select these names and assign to the leader_matrix using the rownames() and colnames() functions respectively:

rownames(leader_matrix) <- leader_nodes$id

colnames(leader_matrix) <- leader_nodes$id

leader_matrix %>% 
  kbl() %>% 
  kable_styling() %>% 
  scroll_box(width = "500px", height = "500px")
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
0 0 1 1 0 0 0 1 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 1 0 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 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
1 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 1 1 0 0 0 0 0 0 1 1 1 1 1 0 0 0 0
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 1 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 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 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 0 0 0 0 0 0 0 0 0 0
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 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
1 0 1 0 1 1 0 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 1 0 1 1 1 1 0 0 0 1 0 0 0 1 1 1 0 1 0 0 0
0 0 0 0 1 0 0 1 0 0 0 0 0 1 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 1 0
0 0 1 0 0 0 1 1 1 0 1 0 0 1 0 0 0 1 1 1 1 0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 0 1 0 1 1 0 1
1 0 0 0 1 1 1 1 1 1 0 1 1 1 1 1 1 0 1 0 1 1 1 0 0 0 0 0 0 0 0 0 1 1 1 0 0 1 0 0 1 0 1
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 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 1 0 1 0 0 0 0 1 0
0 0 1 0 0 0 1 0 1 1 0 0 0 1 1 0 1 0 1 1 0 0 0 0 0 1 0 0 0 1 1 0 0 0 0 0 1 1 0 1 1 0 0
0 0 0 0 1 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 1 0 0 1 0 0 1 0 0 0 1 0 1 0 0 0 1 0 1 0 0 1 1
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 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 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 1 0 0
1 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 1 1 0 0 1 0 1 0 1 1 1 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 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0
1 0 0 0 1 0 1 0 0 1 0 0 0 0 0 0 0 1 1 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 0 0 0 0 0 0 0 0 1 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 1 0 0 0 1 0 0 0 0 0
1 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 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0
0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 1 0 0 0 0 0 0 1 1 1 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 1 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 1 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 1 0
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
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 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 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 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 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 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 1 0 1 1 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 0 0 1 0 0 0 0 1
1 1 1 1 0 0 0 1 0 0 0 0 0 0 0 1 0 1 0 0 0 1 0 1 0 0 1 1 1 0 0 1 0 0 1 1 1 1 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 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
1 0 1 1 0 0 0 1 0 0 0 0 0 0 0 1 0 1 0 0 0 1 0 0 0 0 1 1 0 0 0 0 0 1 0 0 1 1 0 0 0 0 0
0 0 1 0 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 0 1 0 1 1 1 1 0 0 1 0 1 1 1 0 0 1 1 1 1 1 1
1 0 1 0 1 1 0 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 0 0 1 1 1 0 1 0 0 1 1 0 1 1 0 1 1 0 0 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 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 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 0 0
0 0 0 1 0 0 0 0 0 0 1 0 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 1 0 0
0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1
0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 1 1 1 0 0 1 0 1 0 0 1 0

2c. Create Network with Attributes

Edge-lists contain a row for each dyad consisting of at minimum two columns with the name of each actor, and which can also contain other information or attributes about the relationship such as edge weight, timestamps, or other contextual information. Edge-lists also have the advantage of being easier to work with when using network packages in R.

Get Edges

The {igraph} package contains a convenient get.data.frame() function for extracting an edge list from a matrix, but first the matrix must be converted to an igraph network object.

adjacency_matrix <- graph.adjacency(leader_matrix,
                                    diag = FALSE)

class(adjacency_matrix)
## [1] "igraph"
adjacency_matrix
## IGRAPH 89dfef7 DN-- 43 362 -- 
## + attr: name (v/c)
## + edges from 89dfef7 (vertex names):
##  [1] 1 ->3  1 ->4  1 ->8  1 ->28 1 ->36 1 ->38 2 ->28 3 ->1  3 ->8  3 ->16
## [11] 3 ->27 3 ->28 3 ->35 3 ->36 3 ->37 3 ->38 3 ->39 4 ->1  4 ->16 4 ->18
## [21] 4 ->22 4 ->28 4 ->34 4 ->36 5 ->9  6 ->1  7 ->10 7 ->20 8 ->1  8 ->3 
## [31] 8 ->5  8 ->6  8 ->9  8 ->10 8 ->12 8 ->13 8 ->15 8 ->16 8 ->18 8 ->19
## [41] 8 ->21 8 ->22 8 ->23 8 ->25 8 ->26 8 ->27 8 ->28 8 ->32 8 ->36 8 ->37
## [51] 8 ->38 8 ->40 9 ->5  9 ->8  9 ->14 9 ->15 9 ->42 10->3  10->7  10->8 
## [61] 10->9  10->11 10->14 10->18 10->19 10->20 10->21 10->26 10->30 10->34
## [71] 10->38 10->40 10->41 10->43 11->1  11->5  11->6  11->7  11->8  11->9 
## + ... omitted several edges

The diag = FALSE argument converts all values along the diagonal to 0s, thereby removing self-loops. The get.data.frame() function will covert the 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 %>% 
  kbl() %>% 
  kable_styling() %>% 
  scroll_box(width = "200px", height = "500px")
from to
1 3
1 4
1 8
1 28
1 36
1 38
2 28
3 1
3 8
3 16
3 27
3 28
3 35
3 36
3 37
3 38
3 39
4 1
4 16
4 18
4 22
4 28
4 34
4 36
5 9
6 1
7 10
7 20
8 1
8 3
8 5
8 6
8 9
8 10
8 12
8 13
8 15
8 16
8 18
8 19
8 21
8 22
8 23
8 25
8 26
8 27
8 28
8 32
8 36
8 37
8 38
8 40
9 5
9 8
9 14
9 15
9 42
10 3
10 7
10 8
10 9
10 11
10 14
10 18
10 19
10 20
10 21
10 26
10 30
10 34
10 38
10 40
10 41
10 43
11 1
11 5
11 6
11 7
11 8
11 9
11 10
11 12
11 13
11 14
11 15
11 16
11 17
11 19
11 21
11 22
11 23
11 33
11 34
11 35
11 38
11 41
11 43
12 8
13 3
13 5
13 8
13 23
13 27
13 35
13 37
13 42
14 3
14 7
14 9
14 10
14 15
14 17
14 19
14 20
14 26
14 30
14 31
14 37
14 38
14 40
14 41
15 5
15 12
15 14
15 21
15 24
15 27
15 31
15 33
15 37
15 39
15 42
15 43
16 22
17 26
17 41
18 1
18 8
18 10
18 22
18 26
18 28
18 29
18 32
18 34
18 36
18 37
18 38
19 31
20 1
20 5
20 7
20 10
20 18
20 19
20 31
21 11
21 14
21 15
21 33
21 35
22 8
22 16
22 18
22 28
22 29
22 34
22 38
23 1
23 8
23 11
24 3
24 8
24 15
24 27
24 32
24 42
25 3
25 8
25 18
25 24
25 32
25 33
25 34
25 42
26 17
26 41
27 3
27 8
27 15
27 24
27 25
27 30
27 32
27 42
28 1
28 2
28 3
28 4
28 5
28 6
28 7
28 8
28 9
28 10
28 11
28 12
28 13
28 14
28 15
28 16
28 17
28 18
28 19
28 20
28 21
28 22
28 23
28 24
28 25
28 26
28 27
28 29
28 30
28 31
28 32
28 33
28 34
28 35
28 36
28 37
28 38
28 39
28 40
28 41
28 42
28 43
29 18
29 34
31 19
32 24
32 25
32 27
33 6
33 8
33 9
33 12
33 21
33 38
33 43
34 1
34 2
34 3
34 4
34 8
34 16
34 18
34 22
34 24
34 27
34 28
34 29
34 32
34 35
34 36
34 37
34 38
35 13
36 1
36 3
36 4
36 8
36 16
36 18
36 22
36 27
36 28
36 34
36 37
36 38
37 3
37 5
37 6
37 7
37 8
37 9
37 10
37 11
37 12
37 13
37 14
37 15
37 17
37 18
37 19
37 20
37 21
37 23
37 25
37 26
37 27
37 28
37 31
37 33
37 34
37 35
37 38
37 39
37 40
37 41
37 42
37 43
38 1
38 3
38 5
38 6
38 8
38 9
38 10
38 11
38 12
38 13
38 14
38 15
38 18
38 19
38 20
38 21
38 22
38 23
38 26
38 27
38 28
38 30
38 33
38 34
38 36
38 37
38 39
38 40
38 43
41 4
41 11
41 17
41 26
42 9
42 15
42 24
42 25
42 27
42 38
42 43
43 8
43 15
43 21
43 23
43 32
43 33
43 34
43 37
43 39
43 42

Create Network Graph Object

The tbl_graph() function converts the 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 this 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 provided.

  • nodes = expects a data frame, in this case leader_nodes, containing information about the nodes in the graph. If to and/or from are characters or names, like in these 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.

leader_graph <- tbl_graph(edges = leader_edges,
                          nodes = leader_nodes,
                          directed = TRUE)

leader_graph %>% 
  kbl() %>% 
  kable_styling() %>% 
  scroll_box(width = "500px", height = "500px")
id efficacy trust district_site male
1 6.06 4.00 1 0
2 6.56 5.63 1 0
3 7.39 4.63 1 0
4 4.89 4.00 1 0
5 6.06 5.75 0 1
6 7.39 4.38 0 0
7 5.56 3.63 0 1
8 7.50 5.63 1 1
9 7.67 5.25 0 0
10 6.64 4.78 0 0
11 5.06 5.88 0 1
12 7.44 3.75 0 1
13 8.28 5.13 0 0
14 5.67 5.50 0 0
15 6.72 5.63 0 1
16 4.61 3.75 1 0
17 7.17 5.13 0 0
18 5.00 4.13 1 1
19 7.89 3.88 0 1
20 7.17 5.75 0 1
21 7.44 5.38 0 1
22 4.94 3.75 1 0
23 8.50 5.50 0 0
24 5.22 5.13 1 0
25 6.94 4.63 1 0
26 7.00 4.88 0 0
27 5.06 4.50 1 0
28 7.61 4.50 1 1
29 5.67 4.13 1 1
30 5.50 4.88 0 1
31 6.78 4.25 0 1
32 5.88 4.13 1 0
33 5.89 5.13 0 0
34 5.12 4.25 1 1
35 7.44 3.88 0 1
36 7.72 5.75 1 0
37 6.64 4.78 1 1
38 7.56 5.75 1 1
39 8.00 5.63 0 0
40 7.33 5.38 0 0
41 8.17 4.13 0 0
42 8.06 5.25 0 1
43 6.72 3.88 0 0

3. EXPLORE

3a. Examine Descriptive Components

Calculate Node Degree

leader_measures <- leader_graph |>
  activate(nodes) |>
  mutate(in_degree = centrality_degree(mode = "in")) |>
  mutate(out_degree = centrality_degree(mode = "out")) |>
  mutate(degree = degree(leader_graph, mode = "total"))

leader_measures %>% 
  kbl() %>% 
  kable_styling() %>% 
  scroll_box(width = "500px", height = "500px")
id efficacy trust district_site male in_degree out_degree degree
1 6.06 4.00 1 0 12 6 18
2 6.56 5.63 1 0 2 1 3
3 7.39 4.63 1 0 13 10 23
4 4.89 4.00 1 0 5 7 12
5 6.06 5.75 0 1 9 1 10
6 7.39 4.38 0 0 6 1 7
7 5.56 3.63 0 1 6 2 8
8 7.50 5.63 1 1 20 24 44
9 7.67 5.25 0 0 10 5 15
10 6.64 4.78 0 0 9 17 26
11 5.06 5.88 0 1 7 23 30
12 7.44 3.75 0 1 7 1 8
13 8.28 5.13 0 0 6 8 14
14 5.67 5.50 0 0 8 15 23
15 6.72 5.63 0 1 12 12 24
16 4.61 3.75 1 0 8 1 9
17 7.17 5.13 0 0 6 2 8
18 5.00 4.13 1 1 12 12 24
19 7.89 3.88 0 1 9 1 10
20 7.17 5.75 0 1 6 7 13
21 7.44 5.38 0 1 9 5 14
22 4.94 3.75 1 0 9 7 16
23 8.50 5.50 0 0 7 3 10
24 5.22 5.13 1 0 7 6 13
25 6.94 4.63 1 0 6 8 14
26 7.00 4.88 0 0 9 2 11
27 5.06 4.50 1 0 12 8 20
28 7.61 4.50 1 1 11 42 53
29 5.67 4.13 1 1 4 2 6
30 5.50 4.88 0 1 5 0 5
31 6.78 4.25 0 1 6 1 7
32 5.88 4.13 1 0 8 3 11
33 5.89 5.13 0 0 8 7 15
34 5.12 4.25 1 1 12 17 29
35 7.44 3.88 0 1 7 1 8
36 7.72 5.75 1 0 8 12 20
37 6.64 4.78 1 1 11 32 43
38 7.56 5.75 1 1 14 29 43
39 8.00 5.63 0 0 6 0 6
40 7.33 5.38 0 0 6 0 6
41 8.17 4.13 0 0 7 4 11
42 8.06 5.25 0 1 9 7 16
43 6.72 3.88 0 0 8 10 18

Summarize Node Measures

node_measures <- leader_measures |> 
  activate(nodes) |>
  as_tibble()

node_measures %>% 
  kbl() %>% 
  kable_styling() %>% 
  scroll_box(width = "500px", height = "500px")
id efficacy trust district_site male in_degree out_degree degree
1 6.06 4.00 1 0 12 6 18
2 6.56 5.63 1 0 2 1 3
3 7.39 4.63 1 0 13 10 23
4 4.89 4.00 1 0 5 7 12
5 6.06 5.75 0 1 9 1 10
6 7.39 4.38 0 0 6 1 7
7 5.56 3.63 0 1 6 2 8
8 7.50 5.63 1 1 20 24 44
9 7.67 5.25 0 0 10 5 15
10 6.64 4.78 0 0 9 17 26
11 5.06 5.88 0 1 7 23 30
12 7.44 3.75 0 1 7 1 8
13 8.28 5.13 0 0 6 8 14
14 5.67 5.50 0 0 8 15 23
15 6.72 5.63 0 1 12 12 24
16 4.61 3.75 1 0 8 1 9
17 7.17 5.13 0 0 6 2 8
18 5.00 4.13 1 1 12 12 24
19 7.89 3.88 0 1 9 1 10
20 7.17 5.75 0 1 6 7 13
21 7.44 5.38 0 1 9 5 14
22 4.94 3.75 1 0 9 7 16
23 8.50 5.50 0 0 7 3 10
24 5.22 5.13 1 0 7 6 13
25 6.94 4.63 1 0 6 8 14
26 7.00 4.88 0 0 9 2 11
27 5.06 4.50 1 0 12 8 20
28 7.61 4.50 1 1 11 42 53
29 5.67 4.13 1 1 4 2 6
30 5.50 4.88 0 1 5 0 5
31 6.78 4.25 0 1 6 1 7
32 5.88 4.13 1 0 8 3 11
33 5.89 5.13 0 0 8 7 15
34 5.12 4.25 1 1 12 17 29
35 7.44 3.88 0 1 7 1 8
36 7.72 5.75 1 0 8 12 20
37 6.64 4.78 1 1 11 32 43
38 7.56 5.75 1 1 14 29 43
39 8.00 5.63 0 0 6 0 6
40 7.33 5.38 0 0 6 0 6
41 8.17 4.13 0 0 7 4 11
42 8.06 5.25 0 1 9 7 16
43 6.72 3.88 0 0 8 10 18

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         degree     
##  Min.   :0.0000   Min.   : 2.000   Min.   : 0.000   Min.   : 3.00  
##  1st Qu.:0.0000   1st Qu.: 6.000   1st Qu.: 1.500   1st Qu.: 8.50  
##  Median :0.0000   Median : 8.000   Median : 6.000   Median :14.00  
##  Mean   :0.4419   Mean   : 8.419   Mean   : 8.419   Mean   :16.84  
##  3rd Qu.:1.0000   3rd Qu.: 9.500   3rd Qu.:11.000   3rd Qu.:21.50  
##  Max.   :1.0000   Max.   :20.000   Max.   :42.000   Max.   :53.00
skim(node_measures)
Data summary
Name node_measures
Number of rows 43
Number of columns 8
_______________________
Column type frequency:
character 1
numeric 7
________________________
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 8.42 3.16 2.00 6.00 8.00 9.50 20.00 ▁▇▂▁▁
out_degree 0 1 8.42 9.43 0.00 1.50 6.00 11.00 42.00 ▇▂▁▁▁
degree 0 1 16.84 11.55 3.00 8.50 14.00 21.50 53.00 ▇▅▂▁▁

School/District-Level Stats

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  7.52  1.66
## 2             1    18  9.67  4.23

The code below creates a count of the number of nodes in each strong component:

node_measures |>
  group_by(district_site) |>
  summarise(n = n(),
            mean = mean(trust), 
            sd = sd(trust)
            )
## # 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

Similar to our graph of DLT 1, we see this network has a strong component with many members (n=247), and the remaining components are all isolated nodes.

3b. Create a Sociogram

This sociogram highlights how the district-level leadership have much larger, and potentially more influential, networks than school-level leaders. Nine of 18 district-level sites (50%) maintain a network of more than 20 connections while only four of 25 (16 %) maintain networks of similar size at the school level.


4. MODEL

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:

  1. Reciprocity. If one school leader turns to another to discuss something confidential, is the latter likely to reciprocate?

  2. Transitivity. Are school leaders more likely to confide in someone if they both confide in the same school leader?

  3. Attribute Effect. Do a leader’s gender and efficacy score, predict a confidential exchange between two leaders?

4a. Loading Network Data

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= 362 
##     missing edges= 0 
##     non-missing edges= 362 
## 
##  Vertex attribute names: 
##     district_site efficacy male trust vertex.names 
## 
## No edge 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"

4b. Estimate the ERGM Model

Network Structure Parameters

summary(leader_network ~ edges + mutual)
##  edges mutual 
##    362     91

We see from our summary that our leader_network consists of 143 edges and 36 reciprocated dyads.

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.

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   -1.9476     0.0851      0  -22.89   <1e-04 ***
## mutual   1.9597     0.1891      0   10.37   <1e-04 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
##      Null Deviance: 2504  on 1806  degrees of freedom
##  Residual Deviance: 1700  on 1804  degrees of freedom
##  
## AIC: 1704  BIC: 1715  (Smaller is better. MC Std. Err. = 1.814)

The model results suggest that the estimates for the edges and mutual terms are statistically significant. The negative estimate for the edge parameter implies that the probability of a collaborative tie in year 3 is relatively low. The reciprocity parameter, on the other hand, is 1.96 in our model, and indicates a strong tendency for collaborative ties to be reciprocated.

summary(leader_network ~ edges + 
          mutual +
          transitive +
          gwesp(0.25, fixed=T))
##            edges           mutual       transitive gwesp.fixed.0.25 
##         362.0000          91.0000        1662.0000         431.5217

As you can see, we have 1662 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             -5.2419     0.3696      0 -14.181   <1e-04 ***
## mutual             1.4339     0.1846      0   7.768   <1e-04 ***
## gwesp.fixed.0.25   2.3030     0.2856      0   8.063   <1e-04 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
##      Null Deviance: 2504  on 1806  degrees of freedom
##  Residual Deviance: 1556  on 1803  degrees of freedom
##  
## AIC: 1562  BIC: 1578  (Smaller is better. MC Std. Err. = 0.7734)

The model suggest that there is a tendency toward transitivity, that is a collaborative exchange is likely to occur between two people who both have a collaborative exchange with the same individual.

Actor Attribute Parameters

ergm_3 <- ergm(leader_network ~ edges +
                 mutual +
                 gwesp(0.25, fixed=T) +
                 nodefactor('male') +
                 nodecov('trust')
               )
  
summary(ergm_3)
## Call:
## ergm(formula = leader_network ~ edges + mutual + gwesp(0.25, 
##     fixed = T) + nodefactor("male") + nodecov("trust"))
## 
## Monte Carlo Maximum Likelihood Results:
## 
##                   Estimate Std. Error MCMC % z value Pr(>|z|)    
## edges             -5.79332    0.43352      0 -13.363  < 1e-04 ***
## mutual             1.41503    0.19336      0   7.318  < 1e-04 ***
## gwesp.fixed.0.25   2.16033    0.27628      0   7.819  < 1e-04 ***
## nodefactor.male.1  0.15176    0.05284      0   2.872  0.00408 ** 
## nodecov.trust      0.06597    0.03350      0   1.969  0.04894 *  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
##      Null Deviance: 2504  on 1806  degrees of freedom
##  Residual Deviance: 1543  on 1801  degrees of freedom
##  
## AIC: 1553  BIC: 1581  (Smaller is better. MC Std. Err. = 0.9619)

Both gender and trust levels appear to play a significant role in school leaders exchanging collaborative ties.

4c. Check Model Fit

The GOF Function

ergm_3_gof <- gof(ergm_3)

plot(ergm_3_gof)

Overall, the model appears to fit reasonably well in that the black line (the actual observed network measures) closely follows the aggregate measures generated from the simulations, or permutations of the network.


5. COMMUNICATE

Data Visualization

Higher levels of collaboration (degree) and trust are correlated to gender as the blue dots (male participants) have a much larger influence on the top and right portions of the chart. To get a better sense of how this changes when isolating for gender, below is a different plot where these factors have been faceted into separate plots:

When isolating by gender, the male plot depcits collaboration as highly correlated to trust and # of ties to other participants. For the women in the group, however, the trend is slightly negative, implying that there is less trust when the number of relationships grows.

Conclusion

The research question guiding this analysis is:

Controlling for edges, reciprocity, and transitivity, how do gender and trust impact school leader affinity for building strong collaboration networks?

To answer the question, I tested whether school leaders were more likely to send or receive a collaborative exchange looking at two attributes: trust and gender. The results indicate confidential exchanges are impacted more by gender than trust, but both play a role. This makes sense as trust scores are a composite measurement of how trusting a school leader is of multiple individuals (students, parents, colleagues, and principals). Since this research question focused on school leader relationships, it’s not surprising that ‘trust’ was less of an indicator due to its multifaceted character.