Start from clean slate and free up memory
rm(list = ls())
gc()
Load R packages
packages <- c("statnet", "igraph", "RSiena", "EpiModel", "kableExtra", "netdiffuseR",
"sna", "ergm", "coda", "lattice", "plyr", "dplyr", "tidyr", "magrittr",
"mosaic", "snatools", "tidyverse", "ggplot2", "ggnetwork", "visNetwork",
"GGally", "ggraph", "networkD3", "ndtv", "amen", "knitr", "rio", "maps",
"googleAuthR", "ggmap", "smacof", "grid", "rworldmap", "visNetwork", "networkD3",
"ggraph", "tidygraph", "polyclip", "tweenr", "ROAuth", "twitteR", "tweetscores",
"devtools", "streamR", "gganimate", "gifski_renderer", "av", "r2d3")
missing.packages <- which(!packages %in% installed.packages()[, "Package"])
if (length(missing.packages)) install.packages(packages[missing.packages])
lapply(packages, require, character.only = T)
library(devtools)
install_github("pablobarbera/twitter_ideology/pkg/tweetscores")
install.packages("gifski", type = "source")
Creating matrix
m <- matrix(data=1, nrow=5, ncol=4)
dim(m)
## [1] 5 4
m <- matrix(1:10,10,10)
Select matrix elements:
m[2, 3] # Matrix m, row 2, column 3 - a single cell
## [1] 2
m[2, ] # The whole second row of m as a vector
## [1] 2 2 2 2 2 2 2 2 2 2
m[, 2] # The whole second column of m as a vector
## [1] 1 2 3 4 5 6 7 8 9 10
m[1:2, 4:6] # submatrix: rows 1 and 2, columns 4, 5 and 6
## [,1] [,2] [,3]
## [1,] 1 1 1
## [2,] 2 2 2
m[-1, ] # all rows *except* the first one
## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
## [1,] 2 2 2 2 2 2 2 2 2 2
## [2,] 3 3 3 3 3 3 3 3 3 3
## [3,] 4 4 4 4 4 4 4 4 4 4
## [4,] 5 5 5 5 5 5 5 5 5 5
## [5,] 6 6 6 6 6 6 6 6 6 6
## [6,] 7 7 7 7 7 7 7 7 7 7
## [7,] 8 8 8 8 8 8 8 8 8 8
## [8,] 9 9 9 9 9 9 9 9 9 9
## [9,] 10 10 10 10 10 10 10 10 10 10
Import Camp data: - these data were collected by Steve Borgatti, Russ Bernard, Bert Pelto and Gery Ryan at the 1992 NSF Summer Institute on Research Methods in Cultural Anthropology. This was a 3 week course given to 14 carefully selected participants. Network data were collected at the end of each week. These data were collected at the end of the second week. The data were collected by placing each person’s name on a card and asking each respondent to sort the cards in order of how much interaction they had with that person since the beginning of the course (known informally as “camp”). This results in rank order data in which a “1” indicates the most interaction while a “17” indicates the least interaction.
camp92 <- as.matrix(rio::import("./data/camp92.txt"))[,-1]
camp92 <- apply(camp92, 2, as.numeric)
rownames(camp92) <- colnames(camp92)
Transpose matrix:
print(camp92)
## HOLLY BRAZEY CAROL PAM PAT JENNIE PAULINE ANN MICHAEL BILL LEE DON
## HOLLY 0 2 15 8 4 12 10 5 3 11 13 1
## BRAZEY 1 0 12 2 10 11 5 7 9 17 3 8
## CAROL 17 15 0 1 2 4 6 12 7 16 11 10
## PAM 9 5 6 0 3 4 1 2 8 15 16 13
## PAT 4 10 8 3 0 1 2 14 9 16 7 13
## JENNIE 11 9 4 2 1 0 7 3 15 16 10 14
## PAULINE 14 10 5 1 3 4 0 7 6 17 12 11
## ANN 8 9 12 2 10 1 7 0 3 15 17 5
## MICHAEL 3 11 4 6 9 13 7 8 0 17 10 1
## BILL 3 4 8 15 10 16 17 13 2 0 11 1
## LEE 5 6 9 14 7 15 12 17 8 13 0 2
## DON 3 9 13 14 12 17 15 16 1 10 4 0
## JOHN 17 11 1 5 9 15 3 16 8 14 12 6
## HARRY 4 17 5 11 14 16 9 8 1 12 3 2
## GERY 11 6 9 13 15 12 17 16 4 10 8 7
## STEVE 10 9 5 12 8 15 7 14 11 17 3 6
## BERT 7 4 9 6 13 11 14 15 10 16 5 8
## RUSS 2 9 10 17 16 11 15 14 13 3 8 7
## JOHN HARRY GERY STEVE BERT RUSS
## HOLLY 16 9 17 7 6 14
## BRAZEY 15 13 16 6 4 14
## CAROL 3 5 13 8 9 14
## PAM 7 12 17 11 10 14
## PAT 11 12 17 5 6 15
## JENNIE 12 13 17 5 6 8
## PAULINE 2 13 16 9 8 15
## ANN 16 4 13 11 6 14
## MICHAEL 14 2 15 12 5 16
## BILL 9 14 12 6 7 5
## LEE 11 4 16 1 3 10
## DON 11 2 6 8 5 7
## JOHN 0 4 7 10 13 2
## HARRY 6 0 13 10 7 15
## GERY 5 14 0 2 3 1
## STEVE 16 13 4 0 1 2
## BERT 17 12 3 1 0 2
## RUSS 6 12 1 4 5 0
t(camp92)
## HOLLY BRAZEY CAROL PAM PAT JENNIE PAULINE ANN MICHAEL BILL LEE DON
## HOLLY 0 1 17 9 4 11 14 8 3 3 5 3
## BRAZEY 2 0 15 5 10 9 10 9 11 4 6 9
## CAROL 15 12 0 6 8 4 5 12 4 8 9 13
## PAM 8 2 1 0 3 2 1 2 6 15 14 14
## PAT 4 10 2 3 0 1 3 10 9 10 7 12
## JENNIE 12 11 4 4 1 0 4 1 13 16 15 17
## PAULINE 10 5 6 1 2 7 0 7 7 17 12 15
## ANN 5 7 12 2 14 3 7 0 8 13 17 16
## MICHAEL 3 9 7 8 9 15 6 3 0 2 8 1
## BILL 11 17 16 15 16 16 17 15 17 0 13 10
## LEE 13 3 11 16 7 10 12 17 10 11 0 4
## DON 1 8 10 13 13 14 11 5 1 1 2 0
## JOHN 16 15 3 7 11 12 2 16 14 9 11 11
## HARRY 9 13 5 12 12 13 13 4 2 14 4 2
## GERY 17 16 13 17 17 17 16 13 15 12 16 6
## STEVE 7 6 8 11 5 5 9 11 12 6 1 8
## BERT 6 4 9 10 6 6 8 6 5 7 3 5
## RUSS 14 14 14 14 15 8 15 14 16 5 10 7
## JOHN HARRY GERY STEVE BERT RUSS
## HOLLY 17 4 11 10 7 2
## BRAZEY 11 17 6 9 4 9
## CAROL 1 5 9 5 9 10
## PAM 5 11 13 12 6 17
## PAT 9 14 15 8 13 16
## JENNIE 15 16 12 15 11 11
## PAULINE 3 9 17 7 14 15
## ANN 16 8 16 14 15 14
## MICHAEL 8 1 4 11 10 13
## BILL 14 12 10 17 16 3
## LEE 12 3 8 3 5 8
## DON 6 2 7 6 8 7
## JOHN 0 6 5 16 17 6
## HARRY 4 0 14 13 12 12
## GERY 7 13 0 4 3 1
## STEVE 10 10 2 0 1 4
## BERT 13 7 3 1 0 5
## RUSS 2 15 1 2 2 0
campnet <- ifelse(camp92 > 15, 1, 0)
print(campnet)
## HOLLY BRAZEY CAROL PAM PAT JENNIE PAULINE ANN MICHAEL BILL LEE DON
## HOLLY 0 0 0 0 0 0 0 0 0 0 0 0
## BRAZEY 0 0 0 0 0 0 0 0 0 1 0 0
## CAROL 1 0 0 0 0 0 0 0 0 1 0 0
## PAM 0 0 0 0 0 0 0 0 0 0 1 0
## PAT 0 0 0 0 0 0 0 0 0 1 0 0
## JENNIE 0 0 0 0 0 0 0 0 0 1 0 0
## PAULINE 0 0 0 0 0 0 0 0 0 1 0 0
## ANN 0 0 0 0 0 0 0 0 0 0 1 0
## MICHAEL 0 0 0 0 0 0 0 0 0 1 0 0
## BILL 0 0 0 0 0 1 1 0 0 0 0 0
## LEE 0 0 0 0 0 0 0 1 0 0 0 0
## DON 0 0 0 0 0 1 0 1 0 0 0 0
## JOHN 1 0 0 0 0 0 0 1 0 0 0 0
## HARRY 0 1 0 0 0 1 0 0 0 0 0 0
## GERY 0 0 0 0 0 0 1 1 0 0 0 0
## STEVE 0 0 0 0 0 0 0 0 0 1 0 0
## BERT 0 0 0 0 0 0 0 0 0 1 0 0
## RUSS 0 0 0 1 1 0 0 0 0 0 0 0
## JOHN HARRY GERY STEVE BERT RUSS
## HOLLY 1 0 1 0 0 0
## BRAZEY 0 0 1 0 0 0
## CAROL 0 0 0 0 0 0
## PAM 0 0 1 0 0 0
## PAT 0 0 1 0 0 0
## JENNIE 0 0 1 0 0 0
## PAULINE 0 0 1 0 0 0
## ANN 1 0 0 0 0 0
## MICHAEL 0 0 0 0 0 1
## BILL 0 0 0 0 0 0
## LEE 0 0 1 0 0 0
## DON 0 0 0 0 0 0
## JOHN 0 0 0 0 0 0
## HARRY 0 0 0 0 0 0
## GERY 0 0 0 0 0 0
## STEVE 1 0 0 0 0 0
## BERT 1 0 0 0 0 0
## RUSS 0 0 0 0 0 0
# Transposing
t(campnet)
## HOLLY BRAZEY CAROL PAM PAT JENNIE PAULINE ANN MICHAEL BILL LEE DON
## HOLLY 0 0 1 0 0 0 0 0 0 0 0 0
## BRAZEY 0 0 0 0 0 0 0 0 0 0 0 0
## CAROL 0 0 0 0 0 0 0 0 0 0 0 0
## PAM 0 0 0 0 0 0 0 0 0 0 0 0
## PAT 0 0 0 0 0 0 0 0 0 0 0 0
## JENNIE 0 0 0 0 0 0 0 0 0 1 0 1
## PAULINE 0 0 0 0 0 0 0 0 0 1 0 0
## ANN 0 0 0 0 0 0 0 0 0 0 1 1
## MICHAEL 0 0 0 0 0 0 0 0 0 0 0 0
## BILL 0 1 1 0 1 1 1 0 1 0 0 0
## LEE 0 0 0 1 0 0 0 1 0 0 0 0
## DON 0 0 0 0 0 0 0 0 0 0 0 0
## JOHN 1 0 0 0 0 0 0 1 0 0 0 0
## HARRY 0 0 0 0 0 0 0 0 0 0 0 0
## GERY 1 1 0 1 1 1 1 0 0 0 1 0
## STEVE 0 0 0 0 0 0 0 0 0 0 0 0
## BERT 0 0 0 0 0 0 0 0 0 0 0 0
## RUSS 0 0 0 0 0 0 0 0 1 0 0 0
## JOHN HARRY GERY STEVE BERT RUSS
## HOLLY 1 0 0 0 0 0
## BRAZEY 0 1 0 0 0 0
## CAROL 0 0 0 0 0 0
## PAM 0 0 0 0 0 1
## PAT 0 0 0 0 0 1
## JENNIE 0 1 0 0 0 0
## PAULINE 0 0 1 0 0 0
## ANN 1 0 1 0 0 0
## MICHAEL 0 0 0 0 0 0
## BILL 0 0 0 1 1 0
## LEE 0 0 0 0 0 0
## DON 0 0 0 0 0 0
## JOHN 0 0 0 1 1 0
## HARRY 0 0 0 0 0 0
## GERY 0 0 0 0 0 0
## STEVE 0 0 0 0 0 0
## BERT 0 0 0 0 0 0
## RUSS 0 0 0 0 0 0
# Matrix multiplication as sum of vector dot-products
campnet %*% campnet # Multiplying adjacency matrix by itself (A^2). Result? A_ij shows # 2-length paths. Diagonal shows degree of node i. E.g. Calculate entry A_21 and soon realise we're checking for presence of 1s in both second row (Brazey's out-ties) and first column (Holly's in-ties), which is a directed two-path from Brazey to Holly.
## HOLLY BRAZEY CAROL PAM PAT JENNIE PAULINE ANN MICHAEL BILL LEE DON
## HOLLY 1 0 0 0 0 0 1 2 0 0 0 0
## BRAZEY 0 0 0 0 0 1 2 1 0 0 0 0
## CAROL 0 0 0 0 0 1 1 0 0 0 0 0
## PAM 0 0 0 0 0 0 1 2 0 0 0 0
## PAT 0 0 0 0 0 1 2 1 0 0 0 0
## JENNIE 0 0 0 0 0 1 2 1 0 0 0 0
## PAULINE 0 0 0 0 0 1 2 1 0 0 0 0
## ANN 1 0 0 0 0 0 0 2 0 0 0 0
## MICHAEL 0 0 0 1 1 1 1 0 0 0 0 0
## BILL 0 0 0 0 0 0 0 0 0 2 0 0
## LEE 0 0 0 0 0 0 1 1 0 0 1 0
## DON 0 0 0 0 0 0 0 0 0 1 1 0
## JOHN 0 0 0 0 0 0 0 0 0 0 1 0
## HARRY 0 0 0 0 0 0 0 0 0 2 0 0
## GERY 0 0 0 0 0 0 0 0 0 1 1 0
## STEVE 1 0 0 0 0 1 1 1 0 0 0 0
## BERT 1 0 0 0 0 1 1 1 0 0 0 0
## RUSS 0 0 0 0 0 0 0 0 0 1 1 0
## JOHN HARRY GERY STEVE BERT RUSS
## HOLLY 0 0 0 0 0 0
## BRAZEY 0 0 0 0 0 0
## CAROL 1 0 1 0 0 0
## PAM 0 0 1 0 0 0
## PAT 0 0 0 0 0 0
## JENNIE 0 0 0 0 0 0
## PAULINE 0 0 0 0 0 0
## ANN 0 0 1 0 0 0
## MICHAEL 0 0 0 0 0 0
## BILL 0 0 2 0 0 0
## LEE 1 0 0 0 0 0
## DON 1 0 1 0 0 0
## JOHN 2 0 1 0 0 0
## HARRY 0 0 2 0 0 0
## GERY 1 0 1 0 0 0
## STEVE 0 0 0 0 0 0
## BERT 0 0 0 0 0 0
## RUSS 0 0 2 0 0 0
print(campnet)
## HOLLY BRAZEY CAROL PAM PAT JENNIE PAULINE ANN MICHAEL BILL LEE DON
## HOLLY 0 0 0 0 0 0 0 0 0 0 0 0
## BRAZEY 0 0 0 0 0 0 0 0 0 1 0 0
## CAROL 1 0 0 0 0 0 0 0 0 1 0 0
## PAM 0 0 0 0 0 0 0 0 0 0 1 0
## PAT 0 0 0 0 0 0 0 0 0 1 0 0
## JENNIE 0 0 0 0 0 0 0 0 0 1 0 0
## PAULINE 0 0 0 0 0 0 0 0 0 1 0 0
## ANN 0 0 0 0 0 0 0 0 0 0 1 0
## MICHAEL 0 0 0 0 0 0 0 0 0 1 0 0
## BILL 0 0 0 0 0 1 1 0 0 0 0 0
## LEE 0 0 0 0 0 0 0 1 0 0 0 0
## DON 0 0 0 0 0 1 0 1 0 0 0 0
## JOHN 1 0 0 0 0 0 0 1 0 0 0 0
## HARRY 0 1 0 0 0 1 0 0 0 0 0 0
## GERY 0 0 0 0 0 0 1 1 0 0 0 0
## STEVE 0 0 0 0 0 0 0 0 0 1 0 0
## BERT 0 0 0 0 0 0 0 0 0 1 0 0
## RUSS 0 0 0 1 1 0 0 0 0 0 0 0
## JOHN HARRY GERY STEVE BERT RUSS
## HOLLY 1 0 1 0 0 0
## BRAZEY 0 0 1 0 0 0
## CAROL 0 0 0 0 0 0
## PAM 0 0 1 0 0 0
## PAT 0 0 1 0 0 0
## JENNIE 0 0 1 0 0 0
## PAULINE 0 0 1 0 0 0
## ANN 1 0 0 0 0 0
## MICHAEL 0 0 0 0 0 1
## BILL 0 0 0 0 0 0
## LEE 0 0 1 0 0 0
## DON 0 0 0 0 0 0
## JOHN 0 0 0 0 0 0
## HARRY 0 0 0 0 0 0
## GERY 0 0 0 0 0 0
## STEVE 1 0 0 0 0 0
## BERT 1 0 0 0 0 0
## RUSS 0 0 0 0 0 0
campnet %*% t(campnet) # Multiplying by its transpose, what do we get? Ties in common.
## HOLLY BRAZEY CAROL PAM PAT JENNIE PAULINE ANN MICHAEL BILL LEE DON
## HOLLY 2 1 0 1 1 1 1 1 0 0 1 0
## BRAZEY 1 2 1 1 2 2 2 0 1 0 1 0
## CAROL 0 1 2 0 1 1 1 0 1 0 0 0
## PAM 1 1 0 2 1 1 1 1 0 0 1 0
## PAT 1 2 1 1 2 2 2 0 1 0 1 0
## JENNIE 1 2 1 1 2 2 2 0 1 0 1 0
## PAULINE 1 2 1 1 2 2 2 0 1 0 1 0
## ANN 1 0 0 1 0 0 0 2 0 0 0 0
## MICHAEL 0 1 1 0 1 1 1 0 2 0 0 0
## BILL 0 0 0 0 0 0 0 0 0 2 0 1
## LEE 1 1 0 1 1 1 1 0 0 0 2 1
## DON 0 0 0 0 0 0 0 0 0 1 1 2
## JOHN 0 0 1 0 0 0 0 0 0 0 1 1
## HARRY 0 0 0 0 0 0 0 0 0 1 0 1
## GERY 0 0 0 0 0 0 0 0 0 1 1 1
## STEVE 1 1 1 0 1 1 1 1 1 0 0 0
## BERT 1 1 1 0 1 1 1 1 1 0 0 0
## RUSS 0 0 0 0 0 0 0 0 0 0 0 0
## JOHN HARRY GERY STEVE BERT RUSS
## HOLLY 0 0 0 1 1 0
## BRAZEY 0 0 0 1 1 0
## CAROL 1 0 0 1 1 0
## PAM 0 0 0 0 0 0
## PAT 0 0 0 1 1 0
## JENNIE 0 0 0 1 1 0
## PAULINE 0 0 0 1 1 0
## ANN 0 0 0 1 1 0
## MICHAEL 0 0 0 1 1 0
## BILL 0 1 1 0 0 0
## LEE 1 0 1 0 0 0
## DON 1 1 1 0 0 0
## JOHN 2 0 1 0 0 0
## HARRY 0 2 0 0 0 0
## GERY 1 0 2 0 0 0
## STEVE 0 0 0 2 2 0
## BERT 0 0 0 2 2 0
## RUSS 0 0 0 0 0 2
# Element-wise multiplication (both matrices must have same dimension)
campnet * t(campnet) # Note that * is element-wise multiplication. What do we get? Symmetric Adjacency Matrix under the max/strong rule.
## HOLLY BRAZEY CAROL PAM PAT JENNIE PAULINE ANN MICHAEL BILL LEE DON
## HOLLY 0 0 0 0 0 0 0 0 0 0 0 0
## BRAZEY 0 0 0 0 0 0 0 0 0 0 0 0
## CAROL 0 0 0 0 0 0 0 0 0 0 0 0
## PAM 0 0 0 0 0 0 0 0 0 0 0 0
## PAT 0 0 0 0 0 0 0 0 0 0 0 0
## JENNIE 0 0 0 0 0 0 0 0 0 1 0 0
## PAULINE 0 0 0 0 0 0 0 0 0 1 0 0
## ANN 0 0 0 0 0 0 0 0 0 0 1 0
## MICHAEL 0 0 0 0 0 0 0 0 0 0 0 0
## BILL 0 0 0 0 0 1 1 0 0 0 0 0
## LEE 0 0 0 0 0 0 0 1 0 0 0 0
## DON 0 0 0 0 0 0 0 0 0 0 0 0
## JOHN 1 0 0 0 0 0 0 1 0 0 0 0
## HARRY 0 0 0 0 0 0 0 0 0 0 0 0
## GERY 0 0 0 0 0 0 1 0 0 0 0 0
## STEVE 0 0 0 0 0 0 0 0 0 0 0 0
## BERT 0 0 0 0 0 0 0 0 0 0 0 0
## RUSS 0 0 0 0 0 0 0 0 0 0 0 0
## JOHN HARRY GERY STEVE BERT RUSS
## HOLLY 1 0 0 0 0 0
## BRAZEY 0 0 0 0 0 0
## CAROL 0 0 0 0 0 0
## PAM 0 0 0 0 0 0
## PAT 0 0 0 0 0 0
## JENNIE 0 0 0 0 0 0
## PAULINE 0 0 1 0 0 0
## ANN 1 0 0 0 0 0
## MICHAEL 0 0 0 0 0 0
## BILL 0 0 0 0 0 0
## LEE 0 0 0 0 0 0
## DON 0 0 0 0 0 0
## JOHN 0 0 0 0 0 0
## HARRY 0 0 0 0 0 0
## GERY 0 0 0 0 0 0
## STEVE 0 0 0 0 0 0
## BERT 0 0 0 0 0 0
## RUSS 0 0 0 0 0 0
sna::symmetrize(campnet, rule = "weak") # OR rule, if either i->j or i<-j exist;
## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13]
## [1,] 0 0 1 0 0 0 0 0 0 0 0 0 1
## [2,] 0 0 0 0 0 0 0 0 0 1 0 0 0
## [3,] 1 0 0 0 0 0 0 0 0 1 0 0 0
## [4,] 0 0 0 0 0 0 0 0 0 0 1 0 0
## [5,] 0 0 0 0 0 0 0 0 0 1 0 0 0
## [6,] 0 0 0 0 0 0 0 0 0 1 0 1 0
## [7,] 0 0 0 0 0 0 0 0 0 1 0 0 0
## [8,] 0 0 0 0 0 0 0 0 0 0 1 1 1
## [9,] 0 0 0 0 0 0 0 0 0 1 0 0 0
## [10,] 0 1 1 0 1 1 1 0 1 0 0 0 0
## [11,] 0 0 0 1 0 0 0 1 0 0 0 0 0
## [12,] 0 0 0 0 0 1 0 1 0 0 0 0 0
## [13,] 1 0 0 0 0 0 0 1 0 0 0 0 0
## [14,] 0 1 0 0 0 1 0 0 0 0 0 0 0
## [15,] 1 1 0 1 1 1 1 1 0 0 1 0 0
## [16,] 0 0 0 0 0 0 0 0 0 1 0 0 1
## [17,] 0 0 0 0 0 0 0 0 0 1 0 0 1
## [18,] 0 0 0 1 1 0 0 0 1 0 0 0 0
## [,14] [,15] [,16] [,17] [,18]
## [1,] 0 1 0 0 0
## [2,] 1 1 0 0 0
## [3,] 0 0 0 0 0
## [4,] 0 1 0 0 1
## [5,] 0 1 0 0 1
## [6,] 1 1 0 0 0
## [7,] 0 1 0 0 0
## [8,] 0 1 0 0 0
## [9,] 0 0 0 0 1
## [10,] 0 0 1 1 0
## [11,] 0 1 0 0 0
## [12,] 0 0 0 0 0
## [13,] 0 0 1 1 0
## [14,] 0 0 0 0 0
## [15,] 0 0 0 0 0
## [16,] 0 0 0 0 0
## [17,] 0 0 0 0 0
## [18,] 0 0 0 0 0
sna::symmetrize(campnet, rule = "strong") # AND rule, iff i<->j exist;
## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13]
## [1,] 0 0 0 0 0 0 0 0 0 0 0 0 1
## [2,] 0 0 0 0 0 0 0 0 0 0 0 0 0
## [3,] 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
## [5,] 0 0 0 0 0 0 0 0 0 0 0 0 0
## [6,] 0 0 0 0 0 0 0 0 0 1 0 0 0
## [7,] 0 0 0 0 0 0 0 0 0 1 0 0 0
## [8,] 0 0 0 0 0 0 0 0 0 0 1 0 1
## [9,] 0 0 0 0 0 0 0 0 0 0 0 0 0
## [10,] 0 0 0 0 0 1 1 0 0 0 0 0 0
## [11,] 0 0 0 0 0 0 0 1 0 0 0 0 0
## [12,] 0 0 0 0 0 0 0 0 0 0 0 0 0
## [13,] 1 0 0 0 0 0 0 1 0 0 0 0 0
## [14,] 0 0 0 0 0 0 0 0 0 0 0 0 0
## [15,] 0 0 0 0 0 0 1 0 0 0 0 0 0
## [16,] 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
## [18,] 0 0 0 0 0 0 0 0 0 0 0 0 0
## [,14] [,15] [,16] [,17] [,18]
## [1,] 0 0 0 0 0
## [2,] 0 0 0 0 0
## [3,] 0 0 0 0 0
## [4,] 0 0 0 0 0
## [5,] 0 0 0 0 0
## [6,] 0 0 0 0 0
## [7,] 0 1 0 0 0
## [8,] 0 0 0 0 0
## [9,] 0 0 0 0 0
## [10,] 0 0 0 0 0
## [11,] 0 0 0 0 0
## [12,] 0 0 0 0 0
## [13,] 0 0 0 0 0
## [14,] 0 0 0 0 0
## [15,] 0 0 0 0 0
## [16,] 0 0 0 0 0
## [17,] 0 0 0 0 0
## [18,] 0 0 0 0 0
sna::symmetrize(campnet, rule = "upper") # copy the upper triangle over the lower;
## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13]
## [1,] 0 0 0 0 0 0 0 0 0 0 0 0 1
## [2,] 0 0 0 0 0 0 0 0 0 1 0 0 0
## [3,] 0 0 0 0 0 0 0 0 0 1 0 0 0
## [4,] 0 0 0 0 0 0 0 0 0 0 1 0 0
## [5,] 0 0 0 0 0 0 0 0 0 1 0 0 0
## [6,] 0 0 0 0 0 0 0 0 0 1 0 0 0
## [7,] 0 0 0 0 0 0 0 0 0 1 0 0 0
## [8,] 0 0 0 0 0 0 0 0 0 0 1 0 1
## [9,] 0 0 0 0 0 0 0 0 0 1 0 0 0
## [10,] 0 1 1 0 1 1 1 0 1 0 0 0 0
## [11,] 0 0 0 1 0 0 0 1 0 0 0 0 0
## [12,] 0 0 0 0 0 0 0 0 0 0 0 0 0
## [13,] 1 0 0 0 0 0 0 1 0 0 0 0 0
## [14,] 0 0 0 0 0 0 0 0 0 0 0 0 0
## [15,] 1 1 0 1 1 1 1 0 0 0 1 0 0
## [16,] 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
## [18,] 0 0 0 0 0 0 0 0 1 0 0 0 0
## [,14] [,15] [,16] [,17] [,18]
## [1,] 0 1 0 0 0
## [2,] 0 1 0 0 0
## [3,] 0 0 0 0 0
## [4,] 0 1 0 0 0
## [5,] 0 1 0 0 0
## [6,] 0 1 0 0 0
## [7,] 0 1 0 0 0
## [8,] 0 0 0 0 0
## [9,] 0 0 0 0 1
## [10,] 0 0 0 0 0
## [11,] 0 1 0 0 0
## [12,] 0 0 0 0 0
## [13,] 0 0 0 0 0
## [14,] 0 0 0 0 0
## [15,] 0 0 0 0 0
## [16,] 0 0 0 0 0
## [17,] 0 0 0 0 0
## [18,] 0 0 0 0 0
sna::symmetrize(campnet, rule = "lower") # copy the lower triangle over the upper;
## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13]
## [1,] 0 0 1 0 0 0 0 0 0 0 0 0 1
## [2,] 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
## [4,] 0 0 0 0 0 0 0 0 0 0 0 0 0
## [5,] 0 0 0 0 0 0 0 0 0 0 0 0 0
## [6,] 0 0 0 0 0 0 0 0 0 1 0 1 0
## [7,] 0 0 0 0 0 0 0 0 0 1 0 0 0
## [8,] 0 0 0 0 0 0 0 0 0 0 1 1 1
## [9,] 0 0 0 0 0 0 0 0 0 0 0 0 0
## [10,] 0 0 0 0 0 1 1 0 0 0 0 0 0
## [11,] 0 0 0 0 0 0 0 1 0 0 0 0 0
## [12,] 0 0 0 0 0 1 0 1 0 0 0 0 0
## [13,] 1 0 0 0 0 0 0 1 0 0 0 0 0
## [14,] 0 1 0 0 0 1 0 0 0 0 0 0 0
## [15,] 0 0 0 0 0 0 1 1 0 0 0 0 0
## [16,] 0 0 0 0 0 0 0 0 0 1 0 0 1
## [17,] 0 0 0 0 0 0 0 0 0 1 0 0 1
## [18,] 0 0 0 1 1 0 0 0 0 0 0 0 0
## [,14] [,15] [,16] [,17] [,18]
## [1,] 0 0 0 0 0
## [2,] 1 0 0 0 0
## [3,] 0 0 0 0 0
## [4,] 0 0 0 0 1
## [5,] 0 0 0 0 1
## [6,] 1 0 0 0 0
## [7,] 0 1 0 0 0
## [8,] 0 1 0 0 0
## [9,] 0 0 0 0 0
## [10,] 0 0 1 1 0
## [11,] 0 0 0 0 0
## [12,] 0 0 0 0 0
## [13,] 0 0 1 1 0
## [14,] 0 0 0 0 0
## [15,] 0 0 0 0 0
## [16,] 0 0 0 0 0
## [17,] 0 0 0 0 0
## [18,] 0 0 0 0 0
Brief intro to dataset: “The National Longitudinal Study of Adolescent to Adult Health (Add Health) is a longitudinal study of a nationally representative sample of adolescents in grades 7-12 in the United States during the 1994-95 school year. Add Health combines longitudinal survey data on respondents’ social, economic, psychological and physical well-being with contextual data on the family, neighborhood, community, school, friendships, peer groups, and romantic relationships, providing unique opportunities to study how social environments and behaviors in adolescence are linked to health and achievement outcomes in young adulthood.”
Tidyverse grammar: - Selecting: always refers to selecting the columns you want. - Arranging: reorder rows with respect to columns. - Mutating: refers to creating a new variable based on operations peformed on another variable. - Filtering: refers to filtering by rows - Renaming: refers to relabeling column names. - Gathering: refers to gathering columns to transform a wide data set into a long one. - Summarizing: refers to generating summary statitics for a given variable. - Separating: refers to splitting delimited values in one column into multiple columns - Distinct: Eliminates all duplicate values - Joining: refers to merging data sets using key variable.
AHS_WPVAR <- rio::import('./data/ahs_wpvar.csv')
commcnt | sdummy | ego_nid | mfnid_1 | mfnid_2 | mfnid_3 | mfnid_4 | mfnid_5 | mfact_1 | mfact_2 | mfact_3 | mfact_4 | mfact_5 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 1 | 1 | 52 | 99999 | NA | NA | NA | 0 | 0 | NA | NA | NA |
1 | 1 | 2 | 99999 | 33 | 57 | 59 | 62 | 1 | 1 | 1 | 1 | 1 |
1 | 1 | 3 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA |
1 | 1 | 4 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA |
1 | 1 | 5 | 99999 | NA | NA | NA | NA | 2 | NA | NA | NA | NA |
1 | 1 | 6 | 99999 | 19 | 26 | 47 | NA | 1 | 1 | 1 | 0 | NA |
mf stands for “male friend”; ff female friend; mfact stands for # of acts;
AHS_Edges <- AHS_WPVAR %>%
select(ego_nid, mfnid_1:mfnid_5, ffnid_1:ffnid_5, commcnt, sex) %>%
filter(commcnt == 7) %>%
gather(Alter_Label, value, mfnid_1:mfnid_5, ffnid_1:ffnid_5, na.rm = TRUE) %>%
arrange(ego_nid, sex) %>%
filter(value != 99999) %>%
select(ego_nid, value) %>%
rename(Sender = `ego_nid`, Target = `value`)
AHS_Nodes <- AHS_Edges %>%
gather(Alter_Label, value, Sender, Target, na.rm = TRUE) %>%
select(value) %>% rename(ego_nid = `value`)
AHS_Nodes <- AHS_Nodes %>% distinct(ego_nid)
AHS_Nodes <- AHS_Nodes %>% (add_rownames) %>% rename (Sender_ID = rowname) %>%
mutate(Sender_ID = as.numeric(Sender_ID))
AHS_Nodes <- AHS_Nodes %>% rename(Sender = `ego_nid`)
AHS_Edges <- AHS_Edges %>% left_join(AHS_Nodes, by = c("Sender"))
AHS_Nodes <- AHS_Nodes %>% rename(Target = `Sender`, Target_ID = `Sender_ID`)
AHS_Edges <- AHS_Edges %>% left_join(AHS_Nodes, by = c("Target"))
AHS_Edges <- AHS_Edges %>%
select(Sender_ID, Target_ID) %>%
rename(Sender = `Sender_ID`, Target = `Target_ID`)
AHS_Nodes <- AHS_Nodes %>%
rename (ego_nid = `Target`, ID = `Target_ID`)
AHS_Attributes <- AHS_WPVAR %>%
select(commcnt, ego_nid, sex, grade, race5) %>%
filter(commcnt == 7)
AHS_Nodes <- AHS_Nodes %>%
left_join(AHS_Attributes, by = c("ego_nid"))
save(AHS_Edges,file="AHS_Edges.Rda")
save(AHS_Nodes, file="AHS_Nodes.Rda")
number.nodes <- length(unique(c(AHS_Edges$Sender, AHS_Edges$Target)))
ties <- cbind(a=AHS_Edges$Sender, b=AHS_Edges$Target)
ahs.adjmat <- matrix(0, number.nodes, number.nodes)
ahs.adjmat[ties] <- 1
ahs.adjmat[1:15, 1:15]
## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13]
## [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,] 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
## [5,] 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
## [7,] 0 0 0 0 0 0 0 0 0 0 0 1 0
## [8,] 0 0 0 0 0 0 0 0 0 0 0 0 0
## [9,] 0 0 0 0 0 0 0 0 0 0 0 0 0
## [10,] 0 0 0 0 0 0 0 0 0 0 0 0 0
## [11,] 0 0 0 0 0 0 0 0 0 1 0 0 0
## [12,] 0 0 0 0 1 0 1 0 0 0 0 0 0
## [13,] 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
## [15,] 0 0 0 0 0 0 0 0 0 0 0 0 0
## [,14] [,15]
## [1,] 0 0
## [2,] 0 0
## [3,] 0 0
## [4,] 0 0
## [5,] 0 0
## [6,] 0 0
## [7,] 0 0
## [8,] 0 0
## [9,] 0 0
## [10,] 0 0
## [11,] 0 0
## [12,] 0 0
## [13,] 0 0
## [14,] 0 0
## [15,] 0 0
statnet
: from an edgelistprint(AHS_Edges[1:20,])
## Sender Target
## 1 1 108
## 2 1 112
## 3 1 166
## 4 1 113
## 5 1 132
## 6 1 151
## 7 1 204
## 8 2 51
## 9 3 85
## 10 3 117
## 11 3 183
## 12 3 235
## 13 3 56
## 14 3 57
## 15 3 64
## 16 3 182
## 17 3 214
## 18 4 131
## 19 5 243
## 20 6 85
ahs.net <- network::network(AHS_Edges, matrix.type = "edgelist")
print(ahs.net)
## Network attributes:
## vertices = 440
## directed = TRUE
## hyper = FALSE
## loops = FALSE
## multiple = FALSE
## bipartite = FALSE
## total edges= 2099
## missing edges= 0
## non-missing edges= 2099
##
## Vertex attribute names:
## vertex.names
##
## Edge attribute names not shown
statnet
: from an adjacency matrixadj.mat <- network::as.matrix.network(ahs.net)
adj.mat[1:20, 1:20]
## 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
## 1 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
## 3 0 0 0 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
## 5 0 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
## 7 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0
## 8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## 9 0 0 0 0 0 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 0 0 0 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
## 12 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
## 13 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 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
## 16 0 0 0 0 0 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 0 0 0 0 0
## 18 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 0 0
## 20 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
ahs.net <- network::network(adj.mat, matrix.type = "adjacency")
print(ahs.net)
## Network attributes:
## vertices = 440
## directed = TRUE
## hyper = FALSE
## loops = FALSE
## multiple = FALSE
## bipartite = FALSE
## total edges= 2099
## missing edges= 0
## non-missing edges= 2099
##
## Vertex attribute names:
## vertex.names
##
## Edge attribute names not shown
igraph
: from an edgelistprint(as.matrix(AHS_Edges[1:10,]))
## Sender Target
## 1 1 108
## 2 1 112
## 3 1 166
## 4 1 113
## 5 1 132
## 6 1 151
## 7 1 204
## 8 2 51
## 9 3 85
## 10 3 117
ahs.net <- igraph::graph_from_edgelist(as.matrix(AHS_Edges))
print(ahs.net)
## IGRAPH 9045073 D--- 440 2099 --
## + edges from 9045073:
## [1] 1->108 1->112 1->166 1->113 1->132 1->151 1->204 2-> 51
## [9] 3-> 85 3->117 3->183 3->235 3-> 56 3-> 57 3-> 64 3->182
## [17] 3->214 4->131 5->243 6-> 85 6->117 6->167 6->183 6->185
## [25] 6-> 56 6->165 6->179 6->214 7-> 12 7-> 68 7->149 8->111
## [33] 8->183 8->186 8->241 8-> 86 8->215 9->102 9->181 9->207
## [41] 9->236 9-> 55 9-> 56 9-> 67 10->411 10-> 33 11->411 11->243
## [49] 11-> 10 12-> 5 12-> 7 12-> 71 13-> 88 13->216 13->412 14->248
## [57] 14-> 71 14-> 83 14->149 15-> 30 15->130 15->178 15-> 81 15->106
## [65] 15->151 15->215 16->413 16->186 16->220 16-> 57 16->135 16->211
## + ... omitted several edges
Let’s understand the information contained in an igraph
object:
IGRAPH
simply annotates g as an igraph object704e0f7
or whatever follows IGRAPH
is simply how igraph
identifies the g
for itselfD---
refers to descriptive details of g :
U
would tell us that g
is an undirected graphD
tells us that g
is directed graphN
would that g
is a named graph, in that the vertices have a name
attribute--
refers to attributes not applicable to g
, but you may see them in the future:
W
would refer to a weighted graph, where edges have a weight
attributeB
would refer to a bipartite graph, where vertices have a type
attribute440
refers to the number of vertices in g
2099
refers to the number of edges in g
attr
: would display a list of attributes within the graph. There are no attributes in this network. But, in cases where you load networks that use names instead of numbers, you will see name
listed after attr
: . You will see multiple attributes in the future.
(v/c)
, which will appear following name
, tells us that it is a vertex attribute of a character
data type. character is simply what R calls a string
.(e/c)
or (e/n)
referring to edge attributes that are of character or numeric data types(g/c)
or (g/n)
referring to graph attributes that are of character or numeric data types+ edges from 704e0f7:
lists a sample of g ’s edges using the names of the vertices which they connect.igraph
: from an adjacency matrixahs.net <- igraph::graph.adjacency(ahs.adjmat, mode="directed", weighted=NULL, diag=FALSE)
print(ahs.net)
## IGRAPH ba2f6b9 D--- 440 2099 --
## + edges from ba2f6b9:
## [1] 1->108 1->112 1->113 1->132 1->151 1->166 1->204 2-> 51
## [9] 3-> 56 3-> 57 3-> 64 3-> 85 3->117 3->182 3->183 3->214
## [17] 3->235 4->131 5->243 6-> 56 6-> 85 6->117 6->165 6->167
## [25] 6->179 6->183 6->185 6->214 7-> 12 7-> 68 7->149 8-> 86
## [33] 8->111 8->183 8->186 8->215 8->241 9-> 55 9-> 56 9-> 67
## [41] 9->102 9->181 9->207 9->236 10-> 33 10->411 11-> 10 11->243
## [49] 11->411 12-> 5 12-> 7 12-> 71 13-> 88 13->216 13->412 14-> 71
## [57] 14-> 83 14->149 14->248 15-> 30 15-> 81 15->106 15->130 15->151
## [65] 15->178 15->215 16-> 57 16->135 16->186 16->211 16->214 16->220
## + ... omitted several edges
igraph
only package to handle nodelists; so, use edgelists instead.ahs.nodelist <- NULL
for (i in unique(AHS_Edges$Sender)) {
nodelist <- unlist(strsplit(paste(c(i, AHS_Edges$Target[AHS_Edges$Sender ==
i]), collapse = ","), ","))
names(nodelist) <- c("sender", paste0(rep("target", length(nodelist) - 1),
1:(length(nodelist) - 1)))
ahs.nodelist <- plyr::rbind.fill(ahs.nodelist, data.frame(t(as.matrix(nodelist))))
}
ahs.nodelist <- as.matrix(ahs.nodelist)[, -1] # Make sure row.names == sender columns
ahs.nodelist <- split(ahs.nodelist, 1:nrow(ahs.nodelist))
ahs.nodelist <- lapply(ahs.nodelist, function(x) x[!is.na(x)])
print(ahs.nodelist[1:10])
## $`1`
## [1] "108" "112" "166" "113" "132" "151" "204"
##
## $`2`
## [1] "51"
##
## $`3`
## [1] "85" "117" "183" "235" "56" "57" "64" "182" "214"
##
## $`4`
## [1] "131"
##
## $`5`
## [1] "243"
##
## $`6`
## [1] "85" "117" "167" "183" "185" "56" "165" "179" "214"
##
## $`7`
## [1] "12" "68" "149"
##
## $`8`
## [1] "111" "183" "186" "241" "86" "215"
##
## $`9`
## [1] "102" "181" "207" "236" "55" "56" "67"
##
## $`10`
## [1] "411" "33"
ahs.net <- igraph::graph_from_adj_list(ahs.nodelist, mode = "out")
print(ahs.net)
## IGRAPH acde59e D--- 440 2099 --
## + edges from acde59e:
## [1] 1->108 1->112 1->166 1->113 1->132 1->151 1->204 2-> 51
## [9] 3-> 85 3->117 3->183 3->235 3-> 56 3-> 57 3-> 64 3->182
## [17] 3->214 4->131 5->243 6-> 85 6->117 6->167 6->183 6->185
## [25] 6-> 56 6->165 6->179 6->214 7-> 12 7-> 68 7->149 8->111
## [33] 8->183 8->186 8->241 8-> 86 8->215 9->102 9->181 9->207
## [41] 9->236 9-> 55 9-> 56 9-> 67 10->411 10-> 33 11->411 11->243
## [49] 11-> 10 12-> 5 12-> 7 12-> 71 13-> 88 13->216 13->412 14->248
## [57] 14-> 71 14-> 83 14->149 15-> 30 15->130 15->178 15-> 81 15->106
## [65] 15->151 15->215 16->413 16->186 16->220 16-> 57 16->135 16->211
## + ... omitted several edges
statnet
ahs.net <- network::network(AHS_Edges, matrix.type = "edgelist")
# Node attributes
network::set.vertex.attribute(ahs.net, "sex", AHS_Nodes$sex)
library(statnet)
ahs.net %v% "race" <- AHS_Nodes$race5
detach("package:statnet", unload=TRUE)
network::list.vertex.attributes(ahs.net)
## [1] "na" "race" "sex" "vertex.names"
network::get.vertex.attribute(ahs.net, "race")
## [1] 3 1 1 1 3 1 1 1 1 3 1 1 1 1 1 1 1 1 1 5 1 1 1 1 5 1 1 1 1 1 1 1 1 1 1
## [36] 1 1 1 5 1 1 1 1 0 5 5 1 1 1 1 5 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 5 1 1 1
## [71] 0 1 3 1 1 1 3 1 5 1 1 5 1 1 1 1 1 1 1 1 1 1 1 1 1 5 1 1 5 1 5 1 1 1 3
## [106] 1 1 1 1 1 1 1 1 1 1 5 1 3 1 1 1 1 1 5 1 1 5 1 1 1 1 1 1 5 1 1 1 1 3 1
## [141] 1 1 1 1 1 1 1 3 5 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 1 1 1 5 1 1 1 1 1
## [176] 5 1 1 1 1 1 1 3 5 1 1 1 5 1 1 1 1 1 1 1 1 5 3 1 3 5 3 3 5 4 1 1 1 5 1
## [211] 1 1 1 1 1 1 1 1 5 1 1 1 1 1 1 1 1 1 5 1 1 1 1 1 1 1 1 1 5 1 1 1 1 1 3
## [246] 1 1 1 1 1 1 5 1 5 0 1 5 1 1 0 1 1 5 1 1 1 3 1 1 1 1 5 5 1 3 3 1 1 1 5
## [281] 1 1 1 1 3 1 1 1 1 0 1 1 1 1 5 1 1 1 1 3 1 1 1 1 5 3 1 1 1 1 1 1 5 1 1
## [316] 1 5 3 1 4 5 1 1 3 1 1 1 1 0 1 5 5 1 1 1 1 5 1 1 5 1 1 1 1 1 1 5 1 1 5
## [351] 1 1 5 1 1 1 1 1 1 1 1 1 1 1 1 5 1 1 1 1 1 1 1 3 5 3 1 1 5 1 1 5 3 5 3
## [386] 5 1 1 1 1 1 1 5 1 3 1 1 1 1 5 0 1 1 1 1 5 1 1 1 1 1 1 3 1 5 3 3 3 5 5
## [421] 1 5 2 5 3 5 1 1 5 1 5 5 1 1 0 5 1 5 3 1
network::delete.vertex.attribute(ahs.net, "sex")
# Edge attributes
network::set.edge.attribute(ahs.net, "value", 1)
ahs.net %e% "value" <- 1
network::list.edge.attributes(ahs.net)
## [1] "na" "value"
# Network attributes
network::set.network.attribute(ahs.net, "degree distribution", 1:10)
ahs.net <- network::network(AHS_Edges, matrix.type = "edgelist")
AHS_Nodes$gender <- ifelse(AHS_Nodes$sex==1, "M", "F")
network::set.vertex.attribute(ahs.net, "gender", AHS_Nodes$gender)
nodeColors <- ifelse(AHS_Nodes$gender == "M", "hotpink", "dodgerblue")
gender.label <- network::get.vertex.attribute(ahs.net, "gender")
gplot(ahs.net, gmode = "digraph", displaylabels = TRUE, vertex.col = nodeColors,
label = gender.label, label.cex = 0.5, label.col = nodeColors, mode="fruchtermanreingold",
usearrows = FALSE, usecurve = TRUE, edge.curve = 0.1)
head(network::as.matrix.network(ahs.net, matrix.type = "edgelist")) # Edgelist
network::as.matrix.network(ahs.net, matrix.type="adjacency")[1:5, 1:5] # Adjacency Matrix
head(sna::as.edgelist.sna(ahs.net))
sna::as.sociomatrix.sna(ahs.net)[1:5, 1:5]
igraph
ahs.net <- igraph::graph.data.frame(AHS_Edges)
ahs.net <- igraph::set.vertex.attribute(ahs.net, "gender", index=igraph::V(ahs.net), AHS_Nodes$gender)
names(igraph::get.vertex.attribute(ahs.net))
## [1] "name" "gender"
summary(ahs.net)
## IGRAPH fd8ddc4 DN-- 440 2099 --
## + attr: name (v/c), gender (v/c)
igraph::ecount(ahs.net)
## [1] 2099
igraph::vcount(ahs.net)
## [1] 440
igraph::V(ahs.net)[1:10]
## + 10/440 vertices, named, from fd8ddc4:
## [1] 1 2 3 4 5 6 7 8 9 10
igraph::no.clusters(ahs.net)
## [1] 4
igraph::is.connected(ahs.net)
## [1] FALSE
igraph::is.directed(ahs.net)
## [1] TRUE
igraph::get.edgelist(ahs.net)[1:10,]
## [,1] [,2]
## [1,] "1" "108"
## [2,] "1" "112"
## [3,] "1" "166"
## [4,] "1" "113"
## [5,] "1" "132"
## [6,] "1" "151"
## [7,] "1" "204"
## [8,] "2" "51"
## [9,] "3" "85"
## [10,] "3" "117"
ahs_layout <- igraph::layout.fruchterman.reingold(ahs.net)
vertex.colors <- igraph::get.vertex.attribute(ahs.net,"gender")
vertex.colors <- ifelse(vertex.colors == "M", "blue", "pink")
plot(ahs.net, layout=ahs_layout, vertex.color=vertex.colors,
vertex.label=NA, edge.arrow.size=.1, vertex.size=3)
sna_tools
ahs.net <- igraph::graph.data.frame(AHS_Edges)
summary(ahs.net)
## IGRAPH 0f6eab6 DN-- 440 2099 --
## + attr: name (v/c)
ahs.net <- ahs.net %>% snatools::as_network()
print(ahs.net)
## Network attributes:
## vertices = 440
## directed = TRUE
## hyper = FALSE
## loops = FALSE
## multiple = FALSE
## bipartite = FALSE
## total edges= 2099
## missing edges= 0
## non-missing edges= 2099
##
## Vertex attribute names:
## .vrt_id vertex.names
##
## Edge attribute names not shown
ahs.net <- ahs.net %>% snatools::as_igraph()
summary(ahs.net)
## IGRAPH 03ecf9e DN-- 440 2099 --
## + attr: .vrt_id (v/n), name (v/c), .edg_id (e/n)
Suppose we want to analyse how people are connected based on their voting patterns? That is, we want to draw a network where a tie is present between a dyad if they voted the same way on a bill. We need to perform a two-mode to one-mode conversion.
Case in point: Brexit indicative votes. Indicative votes are where MPs vote on a series of options designed to test the will of Parliament to see what, if anything, commands a majority. In the case of Brexit, supporters of indicative votes believe it could provide a way out of the current political stalemate. Usually the government has control over what happens day-to-day in Parliament, but MPs have backed a proposal by a cross-party group of MPs, including Labour’s Hilary Benn and Conservative Sir Oliver Letwin, to take control of the timetable. Under the cross-party plan, MPs can put forward their preferred Brexit plans to Speaker John Bercow, who will select all or some of these options for debate. MPs may be asked to consider whether any plan agreed by Parliament should be put back to the public in another referendum, or whether the UK should stop Brexit altogether by revoking its notification of Article 50. Following a debate on the various options, at 7pm the Commons will be suspended for 30 minutes so MPs can vote on the each plan. During the indicative votes, MPs will enter one of the division lobbies - the corridors in the House of Commons where votes are normally counted - and will receive a paper ballot. They can vote “yes” or “no” on as many options as they are prepared to support.
## Import Brexit indicative votes
brexit.votes <- readRDS("./data/indicative_votes.rds")
brexit.edgelist <- data.frame(brexit.votes %>% select(Member, Party, Vote, description) %>% spread(description, Vote))
brexit.edgelist <- brexit.edgelist[,-ncol(brexit.edgelist)]
colnames(brexit.edgelist) <- c("mp", "party", "baron", "beckett", "boles", "cherry", "clarke", "corbyn", "eusrace", "fysh")
votes <- c("baron", "beckett", "boles", "cherry", "clarke", "corbyn", "eusrace", "fysh")
mp | party | baron | beckett | boles | cherry | clarke | corbyn | eusrace | fysh |
---|---|---|---|---|---|---|---|---|---|
?rfhlaith Begley | Sinn F?in | No Vote Recorded | No Vote Recorded | No Vote Recorded | No Vote Recorded | No Vote Recorded | No Vote Recorded | No Vote Recorded | No Vote Recorded |
Adam Afriyie | Conservative | Aye | No | No | No | No | No | No | Aye |
Adam Holloway | Conservative | Aye | No | No | No | No | No | No | Aye |
Adrian Bailey | Labour | No | Aye | Aye | No Vote Recorded | Aye | Aye | No Vote Recorded | No |
Afzal Khan | Labour | No | Aye | Aye | No Vote Recorded | Aye | Aye | No Vote Recorded | No |
Alan Brown | Scottish National Party | No | Aye | No Vote Recorded | Aye | No Vote Recorded | No Vote Recorded | No | No |
brexit.edgelist <- brexit.edgelist %>% mutate_at(votes, function(x) ifelse(x=="Aye", 1, ifelse(x=="No", 0, NA)))
mp | party | baron | beckett | boles | cherry | clarke | corbyn | eusrace | fysh |
---|---|---|---|---|---|---|---|---|---|
?rfhlaith Begley | Sinn F?in | NA | NA | NA | NA | NA | NA | NA | NA |
Adam Afriyie | Conservative | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
Adam Holloway | Conservative | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
Adrian Bailey | Labour | 0 | 1 | 1 | NA | 1 | 1 | NA | 0 |
Afzal Khan | Labour | 0 | 1 | 1 | NA | 1 | 1 | NA | 0 |
Alan Brown | Scottish National Party | 0 | 1 | NA | 1 | NA | NA | 0 | 0 |
brexit.votes <- data.frame(readRDS("./data/indicative_votes.rds") %>% select(Member, Party, Vote, description))
# Remove non-recorded votes
brexit.votes <- brexit.votes[-grep("No Vote Recorded|No", brexit.votes$Vote),]
brexit.votes <- data.frame(mp=brexit.votes$Member, party=brexit.votes$Party, motion=brexit.votes$description)
brexit.votes[1:10,]
mp | party | motion |
---|---|---|
Wendy Morton | Conservative | Baron (No Deal) |
Nigel Mills | Conservative | Fysh (tariffs) |
Nigel Mills | Conservative | Baron (No Deal) |
Nick Herbert | Conservative | Clarke (CU) |
Nick Herbert | Conservative | Boles (CM2.0) |
Nick Herbert | Conservative | Eustace (EFTA) |
Gloria De Piero | Labour | Corbyn (Lab.) |
Gloria De Piero | Labour | Clarke (CU) |
Gloria De Piero | Labour | Boles (CM2.0) |
Damian Green | Conservative | Clarke (CU) |
brexit.votes.2mode.edgelist <- brexit.votes[,c(1,3)]
brexit.votes.2mode.adj <- as.matrix(table(brexit.votes.2mode.edgelist))
brexit.votes.2mode.adj[1:10, 1:7]
## motion
## mp Baron (No Deal) Beckett (Public vote) Boles (CM2.0)
## Adam Afriyie 1 0 0
## Adam Holloway 1 0 0
## Adrian Bailey 0 1 1
## Afzal Khan 0 1 1
## Alan Brown 0 1 0
## Alan Campbell 0 1 0
## Alan Duncan 0 0 0
## Alan Mak 1 0 0
## Alan Whitehead 0 1 1
## Albert Owen 0 1 1
## motion
## mp Cherry (Revocation) Clarke (CU) Corbyn (Lab.)
## Adam Afriyie 0 0 0
## Adam Holloway 0 0 0
## Adrian Bailey 0 1 1
## Afzal Khan 0 1 1
## Alan Brown 1 0 0
## Alan Campbell 0 1 1
## Alan Duncan 1 0 0
## Alan Mak 0 0 0
## Alan Whitehead 1 1 1
## Albert Owen 1 1 1
## motion
## mp Eustace (EFTA)
## Adam Afriyie 0
## Adam Holloway 0
## Adrian Bailey 0
## Afzal Khan 0
## Alan Brown 0
## Alan Campbell 0
## Alan Duncan 0
## Alan Mak 0
## Alan Whitehead 0
## Albert Owen 0
# Creating igraph object for 2-mode network
brexit.2mode.graph <- igraph::graph.incidence(brexit.votes.2mode.adj, mode="all")
igraph::V(brexit.2mode.graph)$color <- ""
igraph::V(brexit.2mode.graph)$color[1:(igraph::vcount(brexit.2mode.graph)-7)] <- "red"
igraph::V(brexit.2mode.graph)$color[(igraph::vcount(brexit.2mode.graph)-7):igraph::vcount(brexit.2mode.graph)] <- "green"
igraph::V(brexit.2mode.graph)$label <- igraph::V(brexit.2mode.graph)$name
igraph::V(brexit.2mode.graph)$label.color <- rgb(0,0,.2,.5)
igraph::V(brexit.2mode.graph)$label.cex <- .4
igraph::V(brexit.2mode.graph)$size <- 6
igraph::V(brexit.2mode.graph)$frame.color <- NA
igraph::E(brexit.2mode.graph)$color <- rgb(.5,.5,0,.2)
brexit_layout <- igraph::layout.fruchterman.reingold(brexit.2mode.graph)
plot(brexit.2mode.graph, layout=brexit_layout)
To convert a two-mode incidence matrix to a one-mode adjacency matrix, one can simply multiply an incidence matrix by its transpose, which sum the common 1’s between rows.
\[\begin{align*} AB = \left[ \begin{array}{cc} a & b \\ c & d \end{array} \right] \left[ \begin{array}{cc} e & f \\ g & h \end{array} \right] = \left[ \begin{array}{cc} ae+bg & af+bh \\ ce+dg & cf+dh \end{array} \right] \end{align*}\]
Now multiplying the matrix by its transpose:
\[\begin{align*} AA' = \left[ \begin{array}{cc} a & b \\ c & d \end{array} \right] \left[ \begin{array}{cc} a & c \\ b & d \end{array} \right] = \left[ \begin{array}{cc} aa+bb & ac+bd \\ ca+db & cc+dd \end{array} \right] \end{align*}\]
Because our incidence matrix consists of 0’s and 1’s, the off-diagonal entries represent the total number of common columns, which is exactly what we wanted.
# One-mode projection
brexit.votes.1mode.proj <- brexit.votes.2mode.adj %*% t(brexit.votes.2mode.adj)
# Alternatively, use tcrossprod() function, which is much faster as it creates sparse matrices
brexit.votes.1mode.proj[1:5, 1:5]
## mp
## mp Adam Afriyie Adam Holloway Adrian Bailey Afzal Khan
## Adam Afriyie 2 2 0 0
## Adam Holloway 2 2 0 0
## Adrian Bailey 0 0 4 4
## Afzal Khan 0 0 4 4
## Alan Brown 0 0 1 1
## mp
## mp Alan Brown
## Adam Afriyie 0
## Adam Holloway 0
## Adrian Bailey 1
## Afzal Khan 1
## Alan Brown 2
brexit.votes.1mode.graph <- igraph::graph.adjacency(brexit.votes.1mode.proj, mode="undirected", weighted=TRUE, diag=FALSE) # Weighted = TRUE (MPs vote on MULTIPLE motions together)
brexit_layout <- igraph::layout.fruchterman.reingold(brexit.votes.1mode.graph)
party <- data.frame(mp=as.character(igraph::V(brexit.votes.1mode.graph)$name)) %>% left_join(brexit.edgelist[,1:2])
party$party <- as.character(party$party)
party <- party %>% mutate(color = case_when(party=="Conservative" ~ "blue" ,
party=="Democratic Unionist Party" ~ "pink",
party=="Green Party" ~ "green",
party=="Independent" ~ "gray",
party=="Labour" ~ "red",
party=="Liberal Democrat" ~ "orange",
party=="Plaid Cymru" ~ "darkgreen",
party=="Scottish National Party" ~ "yellow"))
igraph::V(brexit.votes.1mode.graph)$party <- as.character(party$party)
igraph::V(brexit.votes.1mode.graph)$color <- as.character(party$color)
igraph::V(brexit.votes.1mode.graph)$label.color <- "black"
igraph::E(brexit.votes.1mode.graph)$width <- igraph::E(brexit.votes.1mode.graph)$weight/20
igraph::E(brexit.votes.1mode.graph)$edge.color <- "gray10"
edge.start <- igraph::ends(brexit.votes.1mode.graph, es=igraph::E(brexit.votes.1mode.graph), names=F)[,1]
edge.col <- igraph::V(brexit.votes.1mode.graph)$color[edge.start]
plot(brexit.votes.1mode.graph, edge.arrow.size=.1, edge.curved=.1, layout=brexit_layout,
vertex.size=3, vertex.label=NA, edge.color=edge.col)
library(ggraph)
library(gganimate)
library(tidygraph)
library(gifski)
library(stringi)
# Amimation without edges
graph <-brexit.votes.1mode.graph %>% as_tbl_graph()
graph
layout_list <- list(
list(layout = 'star'),
list(layout = 'circle'),
list(layout = 'gem'),
list(layout = 'graphopt'),
list(layout = 'grid'),
list(layout = 'mds'),
list(layout = 'randomly'),
list(layout = 'fr'),
list(layout = 'kk'),
list(layout = 'nicely'),
list(layout = 'lgl'),
list(layout = 'drl'))
#filtered_graph <- graph %>% mutate(community = group_walktrap())
layouts <- graph %>%
invoke_map('create_layout', layout_list, graph = .) %>%
set_names(unlist(layout_list)) %>%
bind_rows(.id = 'layout')
dummy_layout <- create_layout(graph, 'nicely')
attr(layouts, 'graph') <- attr(dummy_layout, 'graph')
attr(layouts, 'circular') <- FALSE
# Plot with singular layout + edges
graph %>% ggraph(layout="nicely") +
geom_node_point(aes(col = as.factor(color))) +
geom_edge_arc(alpha = 0.01, aes(edge_width = width/3, col="gray")) +
theme_graph() + theme(legend.position = 'none') +
labs(title = "Commons' Brexit Parties",
subtitle = 'Using nicely layout engine')
ggsave("./output/brexit_toolarge.png")
# Animation with multiple layouts
g <- ggraph(layouts.sub) +
geom_node_point(aes(col = as.factor(color))) +
theme_graph() +
theme(legend.position = 'none') +
labs(title = "Commons' Brexit Parties",
subtitle = 'Using {closest_state} layout engine') +
transition_states(layout, 1, 2) +
ease_aes('linear') +
view_follow()
animated.graph <- animate(g, fps = 30, nframes = 1000)
gganimate::anim_save("brexit_animation.gif", animation = last_animation(), path = "./output/")
save(animated.graph, file="./data/animated_Brexit.RData")