├── README.md ├── Tutorial_Multilayer_Network_Visualization.ipynb ├── Tutorial_SNA_for_Social_Neuroscientists.Rmd ├── adjacency_matrix.csv └── docs └── index.html /README.md: -------------------------------------------------------------------------------- 1 | # Social Network Analysis Tutorials 2 | 3 | This repo contains the two tutorials that accompany the article "Social Network Analysis for Social Neuroscientists" by Baek, Porter, and Parkinson (2020), published in *Social Cognitive and Affective Neuroscience* (https://doi.org/10.1093/scan/nsaa069). In this repo, you will find two tutorials that introduce basic concepts in network analysis for social systems. Both tutorials are geared toward social neuroscientists and researchers of adjacent fields who are interested in learning social network analysis. 4 | 5 | The first tutorial ([html](https://elisabaek.github.io/social_network_analysis_tutorial/) or [R markdown](https://github.com/elisabaek/social_network_analysis_tutorial/blob/master/Tutorial_SNA_for_Social_Neuroscientists.Rmd)) uses the ```igraph``` and ```visNetwork``` packages in R. This tutorial consists of an introductory overview of the following topics: 6 | 7 | * Mathematical representations of networks (e.g., adjacency matrix, edge list) 8 | * Directed and undirected networks 9 | * Visualizations of networks 10 | * Centrality measures 11 | * Degree centrality (in-degree and out-degree) 12 | * Eigenvector centrality 13 | * PageRank centrality 14 | * Betweenness centrality 15 | * Community detection 16 | 17 | The second tutorial ([html](https://elisabaek.github.io/multilayer_network_analysis_tutorial/) or [jupyter notebook](https://github.com/elisabaek/social_network_analysis_tutorial/blob/master/Tutorial_Multilayer_Network_Visualization.ipynb)) uses the ```pymnet``` library in Python. This tutorial consists of an introductory overview of multinetwork visualization, including: 18 | 19 | * Multiplex network visualization 20 | * Multilayer network visualization 21 | 22 | ## Packages used in the tutorials 23 | 24 | * igraph: https://github.com/igraph/igraph 25 | * visNetwork: https://datastorm-open.github.io/visNetwork/ 26 | * pymnet: http://www.mkivela.com/pymnet/ 27 | 28 | ## Funding 29 | 30 | * This material is based upon work supported by the National Science Foundation under Grant No. 1835239 and SBE Postdoctoral Research Fellowship under Grant No. 1911783. 31 | * *Any opinions, findings, and conclusions or recommendations expressed in this material are those of the author(s) and do not necessarily reflect the views of the National Science Foundation.* 32 | -------------------------------------------------------------------------------- /Tutorial_SNA_for_Social_Neuroscientists.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Tutorial for 'Social Network Analysis for Social Neuroscientists'" 3 | author: "This tutorial accompanies the paper 'Social Network Analysis for Social Neuroscientists' by Elisa C. Baek, Mason A. Porter, and Carolyn Parkinson" 4 | output: 5 | html_document: 6 | encoding: UTF-8 7 | number_sections: yes 8 | smart: no 9 | toc: yes 10 | toc_float: yes 11 | knit: (function(input_file, encoding) { 12 | out_dir <- 'docs'; 13 | rmarkdown::render(input_file, 14 | encoding=encoding, 15 | output_file=file.path(dirname(input_file), out_dir, 'index.html'))}) 16 | --- 17 | 18 | # Introduction 19 | 20 | In this tutorial, we go through some key concepts that we discussed in the main text of the paper through an example of an artificial network of a freshman dorm (with _n_ = 50 people). Suppose that we obtained this data by asking participants to identify their friends using a roster-based approach. The result is a network with directed edges, because (as we will see) not all friendship nominations are reciprocated. We use the igraph package for this tutorial and visNetwork for visualization. 21 | 22 | We use the terms "node" and "vertex" interchangeably in this tutorial. We also use the terms "network" and "graph" interchangeably. The igraph package uses "V" to denote nodes (i.e., vertices). 23 | 24 | First, we load the necessary packages. 25 | 26 | ```{r} 27 | library(igraph) 28 | library(visNetwork) 29 | ``` 30 | 31 | # Mathematical Representations 32 | 33 | As we discussed in the main text of the paper, we can represent a network in different ways. We go over some of the ways in this section of the tutorial. 34 | 35 | ## Adjacency Matrix 36 | 37 | Let's load the adjacency matrix of our artificial data. 38 | 39 | ```{r} 40 | adj_mat <- read.csv("https://raw.githubusercontent.com/elisabaek/social_network_analysis_tutorial/master/adjacency_matrix.csv", 41 | header=TRUE,sep=',', row.names = 1,check.names=FALSE) #read in file 42 | adj_mat <- as.matrix(adj_mat) #convert to matrix 43 | ``` 44 | 45 | The adjacency matrix is an _n_ $\times$ _n_ matrix (so in our network, it is 50 $\times$ 50). To make it easier to see the structure of the adjacency matrix, let's look just at the relationship between 5 nodes (i.e., vertices). This represents 5 people in the network and the relationships between them. 46 | 47 | A 1 indicates that there is an edge from _i_ to _j_ (with _i_ $\rightarrow$ _j_). In directed networks such as this one, an edge from _i_ to _j_ does not necessarily imply that there is an edge from _j_ to _i_. Take a look, for instance, at Claire and Derek. Although there is no edge from Claire to Derek (i.e., Claire $\rightarrow$ Derek), there is an edge from Derek to Claire (i.e., Claire $\rightarrow$ Derek). This implies that Claire did not indicate that Derek is a friend, whereas Derek did indicate that Claire is a friend. 48 | 49 | Note additionally that the diagonal of this adjacency matrix consists of all 0s, because individuals were not allowed to indicate themselves as their own friend. 50 | 51 | ```{r} 52 | adj_mat[c(10:15), c(10:15)] 53 | ``` 54 | 55 | We next convert this adjacency matrix to a graph object. 56 | 57 | ```{r} 58 | graph <- graph.adjacency(adj_mat, mode="directed", weighted=NULL) 59 | ``` 60 | 61 | Let's take a look at the graph. 62 | 63 | ```{r} 64 | graph 65 | ``` 66 | 67 | This shows that there are 50 nodes and 745 edges that connect the nodes. The "DN" that precedes these numbers indicates that the network includes directed edges (D) and that the nodes are named (N). 68 | 69 | ## Edge list 70 | 71 | We can also represent the network using an edge list, a list of node pairs that are connected directly by edges. 72 | 73 | ```{r} 74 | edge_list <- as_edgelist(graph) 75 | head(edge_list) 76 | ``` 77 | 78 | ## Nodes and edges 79 | 80 | We also easily retrieve the edges and nodes using the following commands. 81 | 82 | ```{r} 83 | E(graph) #edges 84 | V(graph) #nodes 85 | ``` 86 | 87 | # Visualization 88 | 89 | Now let's visualize our network. 90 | 91 | ## Directed network 92 | 93 | Let's first visualize the network in a way that includes the directions of the edges. 94 | 95 | ```{r} 96 | par(mar = c(0,0,0,0)) #this allows wider margins for the markdown output 97 | plot(graph) 98 | ``` 99 | 100 | As we see, the default parameters in igraph give a visualiation in which it is a bit hard to interpret our data. We can change this by adjusting a few settings. 101 | 102 | ```{r} 103 | par(mar = c(0,0,0,0)) 104 | V(graph)$size <- 3 #changes the size of the nodes 105 | E(graph)$arrow.size <- 0.1 #changes the size of the arrows 106 | E(graph)$width <- 0.5 #changes the width of the edges 107 | E(graph)$color <- "black" #changes the color of the edges 108 | 109 | plot(graph, vertex.label = NA) #graph without the nodes labeled 110 | ``` 111 | 112 | The new visualization of the network is a bit more helpful, and we may be able to notice some features of the network. For instance, we can see that there may be at least 2 dense communities in the network that potentially may represent friendship groups. We can also see that some nodes have many friends, whereas other nodes have few friends. 113 | 114 | We can also use visNetwork to make an interactive network visualization. Here are some useful things to note about using visNetwork: 115 | 116 | * You can zoom in and out of the network using your mouse; this allows you to take a closer look at the different parts of the network. 117 | * To select a node, you can either click on the node in the graph (this will select the corresponding name in the drop-down menu) or use the drop-down menu in the top-left corner (this will select the corresponding node in the graph). 118 | * When you select a node, the edges and other nodes to which that node is connected directly (i.e., "adjacent" in the graph) will also be highlighted. Our settings highlight only the nodes that are connected by a distance of 1 (i.e., connected directly to the node of interest), but you can change this in the code. 119 | 120 | ```{r} 121 | # this helps us define the size of the nodes for better visualization - 122 | # we want all of the nodes to be the same size for now 123 | size <- rep(25,50) 124 | vertex_attr(graph)$size <- size 125 | 126 | visIgraph(graph) %>% 127 | visOptions(highlightNearest = list(enabled = TRUE, degree = 1), # you can specify the distance of ties that would be highlighted when clicking a node 128 | nodesIdSelection = TRUE) %>% 129 | visInteraction(keyboard = TRUE) 130 | 131 | ``` 132 | 133 | ## Undirected network (mutual edges only) 134 | 135 | We may also be interested in looking only at mutually reported friendships. 136 | 137 | ```{r} 138 | graph_mutual <- as.undirected(graph, mode = "mutual") 139 | graph_mutual 140 | ``` 141 | 142 | We see that now there are only 224 edges, and the U indicates that it is an undirected network. Let's visualize this graph. 143 | 144 | ```{r} 145 | par(mar = c(0,0,0,0)) 146 | V(graph_mutual)$size <- 3 #changes the size of the nodes 147 | E(graph_mutual)$arrow.size <- 0.1 #changes the size of the arrows 148 | E(graph_mutual)$width <- 0.5 #changes the width of the edges 149 | E(graph_mutual)$color <- "black" #changes the color of the edges 150 | 151 | plot(graph_mutual, vertex.label = NA) 152 | ``` 153 | 154 | We can also use visNetwork to visualize the graph interactively. Recall that you can zoom in and out of the graph, as well as select a specific node by clicking on it in the graph or through the drop-down menu. 155 | 156 | ```{r} 157 | size <- rep(25,50) 158 | vertex_attr(graph_mutual)$size <- size 159 | 160 | visIgraph(graph_mutual) %>% 161 | visOptions(highlightNearest = list(enabled = TRUE, degree = 1), 162 | nodesIdSelection = TRUE) %>% 163 | visInteraction(keyboard = TRUE) 164 | ``` 165 | 166 | It appears that there may be 3 different friendship groups and that one node (Derek) connects otherwise unconnected individuals, whereas other nodes may be well-connected, with a lot of friends. We can quantify this observation through various centrality measures, as discussed in the main text of the paper. Likewise, we can also use community-detection algorithms to quantify the different number of friendship groups that may exist in this social network. 167 | 168 | ## Some Notes About Visualizations 169 | 170 | It is important to note that although visualizations can be helpful in observing features of a social network, they can be misleading. Additionally, one can use different algorithms to visualize the same network. By default, igraph uses a function called "layout_nicely" to choose a visualization algorithm based on features of a graph. 171 | 172 | Let's try a few different algorithms to plot the undirected network of mutual edges in our example. 173 | 174 | First, we visualize the network using the default layout setting. 175 | 176 | ```{r} 177 | par(mar = c(0,0,0,0)) 178 | V(graph_mutual)$size <- 3 #changes the size of the nodes 179 | E(graph_mutual)$arrow.size <- 0.1 #changes the size of the arrows 180 | E(graph_mutual)$width <- 0.5 #changes the width of the edges 181 | E(graph_mutual)$color <- "black" #changes the color of the edges 182 | plot(graph_mutual, vertex.label = NA) 183 | ``` 184 | 185 | Let's now use a different layout algorithm that places the nodes of the network on a circle. 186 | 187 | ```{r} 188 | par(mar = c(0,0,0,0)) 189 | plot(graph_mutual, layout = layout_in_circle, vertex.label = NA) 190 | ``` 191 | 192 | Let's next try a layout algorithm that randomly places nodes. 193 | 194 | ```{r} 195 | par(mar = c(0,0,0,0)) 196 | plot(graph_mutual, layout = layout_randomly, vertex.label = NA) 197 | ``` 198 | 199 | As these examples demonstrate, the same network can look very different depending on the algorithm that we use. There appeared to be 3 different friendship groups in the first visualization of the network, but the latter two visualizations do not exhibit similar patterns. Accordingly, inferring characteristics of a network from visualizations alone can result in inaccurate perceptions of the network. Therefore, we recommend that researchers quantify network measures of interest and use visualizations as a complement to those calculated measures. 200 | 201 | # Centrality Measures 202 | 203 | As we discussed in the main body of the paper, there are many notions of centrality (i.e., importance) in a network. Different centrality measures are helpful for different questions, and it is important to select the measures that best fit your research questions. 204 | 205 | ## Degree Centrality 206 | 207 | Let's start by calculating degree, the simplest measure of centrality. 208 | 209 | ### Degree Centrality (using both in-degree and out-degree) 210 | 211 | We first look at degree (i.e., the number of direct connections of each node) in the directed graph in our example. Specifically, we calculate the sum of the in-degree and out-degree of each node. By sorting the degrees of the whole network, we can easily identify the individuals with the smallest and largest degrees. 212 | 213 | ```{r} 214 | sort(degree(graph)) 215 | ``` 216 | 217 | We see that Natalie is the person with the fewest connections, whereas Destiny has the most connections. 218 | 219 | We write a function to help visualize visualize the different degrees of different people by scaling node size by degree. 220 | 221 | ```{r} 222 | scalenodes <- function(v,a,b){ 223 | v <- v-min(v) 224 | v <- v/max(v) 225 | v <- v*(b-a) 226 | v+a 227 | } 228 | # set min and max node sizes 229 | min_size_node <- 1 230 | max_size_node <- 7 231 | ``` 232 | 233 | Let's scale the degree values and then visualize the graph. 234 | 235 | ```{r} 236 | par(mar = c(0,0,0,0)) 237 | nodesize_deg <- scalenodes(degree(graph),min_size_node,max_size_node) 238 | 239 | plot(graph, 240 | vertex.size = nodesize_deg, 241 | vertex.label = NA, 242 | edge.color = "#00000088", 243 | edge.curved = .2) 244 | ``` 245 | 246 | From visual inspection, we can probably guess that the largest node, which is located in the hairball-like cluster of nodes, is Destiny and that the tiny node off to the side is Natalie. Let's check this by labeling and changing the node color of only the nodes with the largest and smallest degree centralities. We do this with an "ifelse" statement in the line of the code in which we specify node labeling and node coloring. There are multiple ways to do this; we choose to set the label to NA for all of the nodes that lie between the smallest and largest degrees. 247 | 248 | ```{r} 249 | par(mar = c(0,0,0,0)) 250 | plot(graph, 251 | vertex.size = nodesize_deg, 252 | vertex.label = ifelse ( 253 | degree(graph) < max(degree(graph)) & 254 | degree(graph) > min(degree(graph)), NA, V(graph)$name), 255 | edge.color = "#C9C9C9", 256 | vertex.color = ifelse ( 257 | degree(graph) < max(degree(graph)) & 258 | degree(graph) > min(degree(graph)), "#FCCB51", "#C1F6BC"), 259 | vertex.label.color = "#000000", 260 | vertex.label.family = "Arial", 261 | vertex.label.font = 2, 262 | vertex.label.cex = 1, 263 | edge.curved = .2) 264 | ``` 265 | 266 | We visualize this even more easily with our interactive graph. 267 | 268 | ```{r} 269 | #replace size of each node with its degree 270 | vertex_attr(graph)$size <- nodesize_deg*5 271 | 272 | visIgraph(graph) %>% 273 | visOptions(highlightNearest = list(enabled = TRUE, degree = 1), 274 | nodesIdSelection = TRUE) %>% 275 | visInteraction(keyboard = TRUE) 276 | ``` 277 | 278 | Click on the node that represents Destiny, and then click on the node that represents Natalie. Do you observe the stark difference when you compare what happens when you click on these two nodes? When you click on Destiny, only a small number of nodes fade to gray (i.e., most of the nodes remain colored in blue, indicating that she is connected directly to many people), whereas when you click on Natalie, all but two nodes fade to gray. By clicking on the two nodes that remain colored in blue when we click on Natalie, we see that Natalie is connected directly to Destiny and Dylan. We also retrieve the degree of each individual with these lines of code. 279 | 280 | ```{r} 281 | degree(graph, c("Destiny")) 282 | degree(graph, c("Natalie")) 283 | ``` 284 | 285 | Natalie's degree of 4 comes from the 2 edges that go out (Natalie $\rightarrow$ Destiny and Natalie $\rightarrow$ Dylan) and the 2 edges that come in (Destiny $\rightarrow$ Natalie and Dylan $\rightarrow$ Natalie). 286 | 287 | Throughout this tutorial, we will continue to label and color in green the node(s) with the largest and smallest centrality values. This will help us see how different centrality measures identify different nodes as important. 288 | 289 | ### In-Degree Centrality 290 | 291 | What if we are interested in popularity, as quantified by how many people reported that they are friends with an individual? We can calculate this with in-degree, the number of edges that point to a node. Let's take a look at the in-degree centrality values, which we sort from smallest to largest. 292 | 293 | ```{r} 294 | sort(degree(graph, mode = c("in"))) 295 | ``` 296 | 297 | This has a similar pattern as what we observed by calculating degree by summing incoming (i.e., in-edge) and outgoing (i.e., out-edge) edges. With an in-degree centrality of 2, Natalie has the smallest number of people who reported being friends with her. (As we saw in the interactive graph above, these people are Destiny and Dylan.) Destiny has the largest number of people who reported being friends with her; she has an in-degree centrality value of 36. We also note Joshua, who has an in-degree centrality of 13 (as 13 people reported that they are friends with him). We will compare the centrality values of these 3 individuals in the next few sections. 298 | 299 | Let's now visualize our network by drawing only edges that point towards nodes, with node size representing the size of the in-degree centralities. We will also label and color in green the nodes with the largest and smallest in-degree centralities. 300 | 301 | ```{r} 302 | nodesize_indegree <- scalenodes(degree(graph,mode = c("in")), min_size_node, max_size_node) 303 | 304 | par(mar = c(0,0,0,0)) 305 | plot(graph, 306 | vertex.size = nodesize_indegree, 307 | vertex.label = ifelse( 308 | degree(graph, mode = c("in")) < max(degree(graph,mode = c("in"))) & 309 | degree(graph, mode = c("in")) > min(degree(graph,mode = c("in"))), NA, V(graph)$name), 310 | edge.color = "#C9C9C9", 311 | vertex.color = ifelse ( 312 | degree(graph, mode = c("in")) < max(degree(graph, mode = c("in"))) & 313 | degree(graph, mode = c("in")) > min(degree(graph, mode = c("in"))), "#FCCB51", "#C1F6BC"), 314 | vertex.label.color = "#000000", 315 | vertex.label.family = "Arial", 316 | vertex.label.font = 2, 317 | edge.curved = .2) 318 | ``` 319 | 320 | We again visualize the graph interactively. The node size in this interactive graph also reflects each individual node's in-degree centrality value. Note that the current version of the visNetwork package (version 2.0.8) does not does not allow the ability to highlight only in-edges or out-edges when clicking on a node. Therefore, when selecting Natalie's node, although the node size reflects her in-degree centrality value, you will see both in-edges and out-edges. 321 | 322 | ```{r} 323 | #replace size of each node with its degree 324 | vertex_attr(graph)$size <- nodesize_indegree*5 325 | 326 | visIgraph(graph) %>% 327 | visOptions(highlightNearest = list(enabled = TRUE, degree = 1), 328 | nodesIdSelection = TRUE) %>% 329 | visInteraction(keyboard = TRUE) 330 | 331 | ``` 332 | 333 | Take another look at Destiny and Natalie; it should be pretty easy to identify these nodes based on their sizes. Take a look at Joshua as well (it is probably easiest to do so from the drop-down menu), and observe that there are 13 edges that point to his node. 334 | 335 | ### Out-Degree Centrality 336 | 337 | We can also see how many times people were named by others as friends by calculating the out-degrees of the network. Let's first look at the out-degree values, which we sort from smallest to largest. 338 | 339 | ```{r} 340 | sort(degree(graph, mode = c("out"))) 341 | ``` 342 | 343 | Note that Natalie, Destiny, and Joshua all have the same out-degree as their in-degree (36, 2, and 13, respectively). We verify this observation by retrieving each individual's in-degree and out-degree values. 344 | 345 | ```{r} 346 | # Destiny's in-degree and out-degree 347 | degree(graph, mode = c("in"), c("Destiny")) 348 | degree(graph, mode = c("out"), c("Destiny")) 349 | 350 | # Natalie's in-degree and out-degree 351 | degree(graph, mode = c("in"), c("Natalie")) 352 | degree(graph, mode = c("out"), c("Natalie")) 353 | 354 | # Joshua's in-degree and out-degree 355 | degree(graph, mode = c("in"), c("Joshua")) 356 | degree(graph, mode = c("out"), c("Joshua")) 357 | ``` 358 | 359 | Because it is easy to visualize Natalie's direct connections (because she has only 2 friends), we already saw earlier that Natalie's in-degree and out-degree of 2 reflect her in-edges and out-edges with Destiny and Dylan. 360 | 361 | However, we also want to see whether whether something similar is true for Destiny and Joshua. It is possible that people who Joshua reported as his friends are not the same people who listed Joshua as a friend. 362 | 363 | Let's visualize the network again, but this time we'll draw just the out-edges. The size of the nodes reflect out-degree values, which correspond to the number of friends that each individual reported. We label and color in green the nodes with the smallest and largest out-degree centrality values. 364 | 365 | ```{r} 366 | nodesize_outdegree <- scalenodes(degree(graph, mode = c("out")), min_size_node, max_size_node) 367 | 368 | par(mar = c(0,0,0,0)) 369 | plot(graph, 370 | vertex.size = nodesize_outdegree, 371 | vertex.label = ifelse( 372 | degree(graph, mode = c("out")) < max(degree(graph, mode = c("out"))) & 373 | degree(graph, mode = c("out")) > min(degree(graph, mode = c("out"))), NA, V(graph)$name), 374 | edge.color = "#C9C9C9", 375 | vertex.color = ifelse ( 376 | degree(graph, mode = c("out")) < max(degree(graph, mode = c("out"))) & 377 | degree(graph, mode = c("out")) > min(degree(graph, mode = c("out"))), 378 | "#FCCB51", "#C1F6BC"), 379 | vertex.label.color = "#000000", 380 | vertex.label.family = "Arial", 381 | vertex.label.font = 2, 382 | edge.curved = .2) 383 | ``` 384 | 385 | We also visualize the network interactively. Recall that you can zoom in and out, and you can click on the nodes in the plot or through the drop-down menu. Take a look at Natalie, Destiny, and Joshua. 386 | 387 | ```{r} 388 | #replace size of each node with its degree 389 | vertex_attr(graph)$size <- nodesize_outdegree*5 390 | 391 | visIgraph(graph) %>% 392 | visOptions(highlightNearest = list(enabled = TRUE, degree = 1), 393 | nodesIdSelection = TRUE) %>% 394 | visInteraction(keyboard = TRUE) 395 | ``` 396 | 397 | ### Degree Centrality (using only reciprocated edges) 398 | 399 | We now look at degree centrality by only using reciprocated edges (i.e., only counting a tie between two nodes if both nodes reported it). This lets us answer the question that we posed above about whether Joshua and Destiny's equal in-degree and out-degree values reflect reciprocated friendships. 400 | 401 | We start by looking at degree centrality values, which we sort from smallest to largest. 402 | 403 | ```{r} 404 | sort(degree(graph_mutual)) 405 | ``` 406 | 407 | Let's compare Destiny and Joshua's degree centrality values using only reciprocated edges with their degree centrality values using in-edges and out-edges. 408 | 409 | ```{r} 410 | # Destiny's in-degree and out-degree 411 | degree(graph, mode = c("in"), c("Destiny")) 412 | degree(graph, mode = c("out"), c("Destiny")) 413 | 414 | # Destiny's degree centrality calculated from reciprocated edges only: 415 | degree(graph_mutual, c("Destiny")) 416 | 417 | # Joshua's in-degree and out-degree 418 | degree(graph, mode = c("in"), c("Joshua")) 419 | degree(graph, mode = c("out"), c("Joshua")) 420 | 421 | # Joshua's degree centrality calculated from reciprocated edges only: 422 | degree(graph_mutual, c("Joshua")) 423 | ``` 424 | 425 | We see that Destiny's degree centrality value from reciprocated edges equals both her in-degree and out-degree centrality values, so all of her friendships are reciprocated. 426 | 427 | By contrast, we see that even though Joshua has the same in-degree and out-degree centrality values of 13, his degree centrality value from reciprocated edges edges is 5. This seems to suggest that the majority of his friendships may be misaligned, in that the people who he listed as friends do not list him as a friend, and vice versa (which may be an interesting phenomenon in itself, in reference to cognitive social structures). 428 | 429 | We now visualize the network by drawing only reciprocated edges. The size of each node corresponds to its degree centrality calculated from reciprocated edges only. Similar to the previous examples, we label and color in green the individual nodes with the largest and smallest degree centrality values. 430 | 431 | ```{r} 432 | nodesize_deg_mutual <- scalenodes(degree(graph_mutual), min_size_node, max_size_node) 433 | 434 | par(mar = c(0,0,0,0)) 435 | plot(graph_mutual, 436 | vertex.size = nodesize_deg_mutual, 437 | vertex.label = ifelse(degree(graph_mutual) < max(degree(graph_mutual)) & 438 | degree(graph_mutual) > min(degree(graph_mutual)), NA, 439 | V(graph_mutual)$name), 440 | edge.color = "#C9C9C9", 441 | vertex.color = ifelse ( 442 | degree(graph_mutual) < max(degree(graph_mutual)) & 443 | degree(graph_mutual) > min(degree(graph_mutual)), "#FCCB51", "#C1F6BC"), 444 | vertex.label.color = "#000000", 445 | vertex.label.family = "Arial", 446 | vertex.label.cex = 1, 447 | edge.curved = .2) 448 | ``` 449 | 450 | We visualize this graph interactively. 451 | 452 | ```{r} 453 | #replace size of each node with its degree 454 | vertex_attr(graph_mutual)$size <- nodesize_deg_mutual*5 455 | 456 | visIgraph(graph_mutual) %>% 457 | visOptions(highlightNearest = list(enabled = TRUE, degree = 1), 458 | nodesIdSelection = TRUE) %>% 459 | visInteraction(keyboard = TRUE) 460 | ``` 461 | 462 | Take a look at Joshua. You can see that the size of his node in this visualization is considerably smaller than in previous graphs. Additionally, by zooming in, we can see his 5 mutual ties (Ian, Destiny, Thomas, Taylor, and Talliea). 463 | 464 | As this example demonstrates, even seemingly small variations in calculating and conceptualizing types of degree centrality can lead to qualitatively different conclusions, so it is important to carefully choose the appropriate measures for your research questions. 465 | 466 | ## Eigenvector Centrality 467 | 468 | Let's next calculate eigenvector centrality, which captures how well-connected a node is to other well-connected nodes. For our example, it seems sensible to calculate eigenvector centrality using in-degree (i.e., for incoming ties), because we are interested in measuring prestige. 469 | 470 | As in previous examples, let's first take a look at the eigenvector centrality values, which we sort from smallest to largest. 471 | 472 | ```{r} 473 | sort(eigen_centrality(graph, directed = TRUE)$vector) 474 | ``` 475 | 476 | Take a look at Natalie. Recall that she has the smallest degree centrality for all types of degree centrality that we calculated. However, when calculating eigenvector centrality, we see that Natalie is not the lowest-ranked person. Why is this the case? Let's see if visualizing the network helps us figure out the reason. We give the nodes sizes that correspond to their eigenvector centralities, and we label and color in green the inividuals with the largest and smallest eigenvector centrality values. 477 | 478 | ```{r} 479 | nodesize_eigen <- scalenodes(eigen_centrality(graph,directed=TRUE)$vector, min_size_node, max_size_node) 480 | 481 | par(mar = c(0,0,0,0)) 482 | 483 | plot(graph, 484 | vertex.size = nodesize_eigen, 485 | edge.color = "#C9C9C9", 486 | vertex.color = ifelse ( 487 | eigen_centrality(graph,directed = TRUE)$vector < 1.00 & 488 | eigen_centrality(graph,directed = TRUE)$vector > 0.02062163, "#FCCB51", "#C1F6BC"), 489 | vertex.label = ifelse(eigen_centrality(graph,directed = TRUE)$vector < 0.99 & 490 | eigen_centrality(graph,directed = TRUE)$vector > 0.02062163, NA, 491 | V(graph)$name), 492 | vertex.label.family = "Arial", 493 | vertex.label.font = 2, 494 | vertex.label.cex = 1, 495 | edge.curved = .2) 496 | ``` 497 | 498 | We see that the individuals with the smallest eigenvector centralities are those who are friends with one another, but are not well-connected to well-connected others. Destiny has the largest eigenvector centrality, as she is well-connected to well-connected others. 499 | 500 | Let's take a look at the interactive graph, which allows us to select individuals and zoom in on them. 501 | 502 | ```{r} 503 | #replace size of each node with its eigenvector centrality 504 | vertex_attr(graph)$size <- nodesize_eigen*5 505 | 506 | visIgraph(graph) %>% 507 | visOptions(highlightNearest = list(enabled = TRUE, degree = 1), 508 | nodesIdSelection = TRUE) %>% 509 | visInteraction(keyboard = TRUE) 510 | ``` 511 | 512 | We can see that Destiny is a popular person in a popular group. Notice that Natalie's node is larger than those of Anna, Damien, Cameron, Alec, Amber, Amy, and Christopher. This indicates that her eigenvector centrality is larger than those of these other people. However, when we look at in-degree centrality alone, we see that each of these individuals has a larger in-degree value than Natalie. 513 | 514 | ```{r} 515 | degree(graph, mode = c("in"), c("Damien")) 516 | ``` 517 | 518 | For example, take a look at Damien. Damien has an in-degree of 11, which is larger than Natalie's in-degree (which we recall is 2). However, Damien's eigenvector centrality value is smaller than Natalie's. 519 | 520 | ```{r} 521 | eigen_centrality_values <- eigen_centrality(graph, directed = TRUE)$vector 522 | eigen_centrality_values[c("Damien")] 523 | eigen_centrality_values[c("Natalie")] 524 | ``` 525 | 526 | This is the case because Natalie is well-connected with well-connected others. (In this case, her two friends are Destiny and Dylan, who are well-connected.) Another way to think about this is that Natalie is friends with the most popular person (i.e., Destiny) in the most popular group in the network. 527 | 528 | The members of the network with the smallest eigenvector centrality values are people who seem to be in a group by themselves. These people are friends with one another, but they are not well-connected to well-connected others. Another way to think about this is that these individuals are part of an unpopular friendship group in the network. Through this example, we can see that eigenvector centrality gives information that is not captured by degree centrality (which counts the number of friends of an individual). 529 | 530 | Let's look at one more example that demonstrates that eigenvector centrality gives information that is not captured by degree centrality. Consider Bryce and Jessica, who have the same in-degree centrality of 12. 531 | 532 | ```{r} 533 | degree(graph, mode = c("in"), c("Bryce")) 534 | degree(graph, mode = c("in"), c("Jessica")) 535 | ``` 536 | 537 | However, their eigenvector centralities differ rather substantially. 538 | 539 | ```{r} 540 | eigen_centrality_values[c("Bryce")] 541 | eigen_centrality_values[c("Jessica")] 542 | ``` 543 | 544 | Bryce has an eigenvector centrality value of only 0.045, whereas Jessica has an eigenvector centrality value of 0.408, which is almost 10 times larger. What is happening in this example? 545 | 546 | Go back to the interactive graph, and use the drop-down menu to look at what happens when you select Bryce and then Jessica. You can see that none of Bryce's connections are well-connected to well-connected others (they are friends with one another in the unpopular group), whereas Jessica's connections are well-connected to well-conneted others (they are friends with people in the popular group). 547 | 548 | These examples highlight the importance of being driven by one's research questions to select which network measures are most appropriate, as well as carefully considering the inferences that one can draw from these measures. 549 | 550 | ## PageRank Centrality 551 | 552 | We also calculate PageRank centrality, which is a variation of eigenvector centrality that has been used most famously for ranking search results on the World Wide Web. A node tends to have a large PageRank centrality if it has large in-degree (i.e., many nodes point to it) and if the in-edges are from nodes that themselves have large in-degrees. The difference between PageRank centrality and eigenvector centrality is that the former incorporates an additional "teleportation" factor that augments the network structure. By default, the igraph package uses the "PRPACK" library for its algorithm. More information on the algorithm used is available at https://igraph.org/r/doc/page_rank.html and https://github.com/dgleich/prpack. 553 | 554 | Let's start by sorting the PageRank centrality values from smallest to largest. We use the default teleportation strategy (which allows teleportation to all nodes, with the same probability for each node), with the default teleportation probability of 0.15. 555 | 556 | ```{r} 557 | sort(page_rank(graph,directed = TRUE)$vector) 558 | ``` 559 | 560 | Note that Natalie has the smallest PageRank centrality value, although we just saw in in Section 4.2 that she does not have the smallest eigenvector centrality value. This arises from the presence of teleportation in calculating PageRank centrality. To explore this difference further, one can recalculate PageRank for teleportation probabilities that are progressively closer to 0. 561 | 562 | Let's visualize the network, with the node sizes reflecting the sizes of the PageRank centrality values. We label and color in green the nodes with the smallest and largest PageRank centrality values. 563 | 564 | ```{r} 565 | nodesize_pagerank <- scalenodes(page_rank(graph,directed = TRUE)$vector, min_size_node, max_size_node) 566 | 567 | par(mar = c(0,0,0,0)) 568 | 569 | plot(graph, 570 | vertex.size = nodesize_pagerank, 571 | edge.color = "#C9C9C9", 572 | vertex.color = ifelse ( 573 | page_rank(graph, directed = TRUE)$vector < max(page_rank(graph, directed = TRUE)$vector) & 574 | page_rank(graph, directed = TRUE)$vector > min(page_rank(graph, directed = TRUE)$vector), 575 | "#FCCB51", "#C1F6BC"), 576 | vertex.label = ifelse( 577 | page_rank(graph, directed = TRUE)$vector < 578 | max(page_rank(graph, directed = TRUE)$vector) & 579 | page_rank(graph, directed = TRUE)$vector > 580 | min(page_rank(graph, directed = TRUE)$vector), NA, V(graph)$name), 581 | vertex.label.family = "Arial", 582 | vertex.label.font = 2, 583 | vertex.label.cex = 1, 584 | edge.curved = .2) 585 | ``` 586 | 587 | We also plot the graph interactively. The sizes of the nodes reflect their PageRank centrality values. 588 | 589 | ```{r} 590 | #replace size of each node with its pagerank centrality 591 | vertex_attr(graph)$size <- nodesize_pagerank*5 592 | 593 | visIgraph(graph) %>% 594 | visOptions(highlightNearest = list(enabled = TRUE, degree = 1), 595 | nodesIdSelection = TRUE) %>% 596 | visInteraction(keyboard = TRUE) 597 | ``` 598 | 599 | ## Betweenness Centrality 600 | 601 | Let's next calculate (geodesic) betweenness centrality, which measures the extent to which shortest paths between nodes traverse a node. We start by sorting nodes according to their betweenness centralities. 602 | 603 | ```{r} 604 | sort(betweenness(graph,directed = TRUE)) 605 | ``` 606 | 607 | There are multiple people who have a between centrality value of 0, indicating that they do not lie on a shortest path between any other two nodes in the network. Observe that Destiny does not have the largest betweenness centrality value, even though she has the largest in-degree, out-degree, eigenvector, and PageRank centrality values. 608 | 609 | Let's visualize the graph, with the node sizes now reflecting their betweenness centrality values. We label and color in green the nodes with the smallest and largest betweenness centrality values. 610 | 611 | ```{r} 612 | nodesize_betweenness <- scalenodes(betweenness(graph,directed = TRUE), min_size_node, max_size_node) 613 | 614 | par(mar = c(0,0,0,0)) 615 | 616 | plot(graph, 617 | vertex.size = nodesize_betweenness, 618 | edge.color = "#C9C9C9", 619 | vertex.color = ifelse ( 620 | betweenness(graph) < max(betweenness(graph)) & 621 | betweenness(graph) > min(betweenness(graph)), "#FCCB51", "#C1F6BC"), 622 | vertex.label.color = "#000000", 623 | vertex.label = ifelse(betweenness(graph) < max(betweenness(graph)) & betweenness(graph) > min(betweenness(graph)), NA, 624 | V(graph)$name), 625 | vertex.label.family = "Arial", 626 | vertex.label.font = 2, 627 | vertex.label.cex = 1, 628 | edge.curved = .2) 629 | ``` 630 | 631 | Notice how Natalie, Amy, Damien, and Alec do not appear to be connected directly to different groups of friends. However, Derek seems to play an important role in connecting two different groups of friends. We will make this observation clearer by looking at the graph interactively. 632 | 633 | ```{r} 634 | #replace size of each node with its betweenness 635 | vertex_attr(graph)$size <- nodesize_betweenness*5 636 | 637 | visIgraph(graph) %>% 638 | visOptions(highlightNearest = list(enabled = TRUE, degree = 1), 639 | nodesIdSelection = TRUE) %>% 640 | visInteraction(keyboard = TRUE) 641 | ``` 642 | 643 | Let's take a look at Derek. Derek has the largest betweenness centrality, but his eigenvector centrality is near the median value. 644 | 645 | ```{r} 646 | sort(eigen_centrality_values) 647 | ``` 648 | 649 | Derek has an eigenvector centrality value of 0.427, which is neither near the largest value nor near the smallest one. Therefore, although Derek seems to be friends with people who are not friends with one another, he isn't necessarily well-connected to well-connected others. As this example demonstrates, it is possible for individuals who have large betweenness centrality to be on the periphery of multiple friendship groups. 650 | 651 | This example again highlights that different centrality measures capture different aspects of importance in a network, and it is important to carefully consider your research questions when determining which centrality measures to study. 652 | 653 | # Community Detection 654 | 655 | We next identify communities in the network. As we noted in the main text of the paper, there are numerous algorithms that one can use for community detection. Although an in-depth overview of available algorithms is beyond the scope of the main manuscript text and this tutorial, we discuss two different algorithms that are readily available. 656 | 657 | Let's first detect communities using the "fast greedy" method that uses the Clauset--Newman--Moore algorithm. This algorithm seeks to maximize an objective function known as "modularity", which measures the extent to which nodes in a community connect densely with each other compared to their connections with nodes in other densely connected communities. For simplicity, we use a version of our network that consists of only reciprocated ties. (We also note that that this algorithm is an outdated approach for maximizing modularity, and our presentation is only an illustration.) 658 | 659 | We label communities by coloring the nodes, and we use a colored circle to indicate the community of each node. 660 | 661 | One potentially interesting question is where Derek, our large-betweenness-centrality person who connects multiple friendship groups, is assigned to communities by different community-detection algorithms. We label his node so that it is easier to track. 662 | 663 | ```{r} 664 | size <- rep(25,50) 665 | vertex_attr(graph_mutual)$size <- size 666 | 667 | par(mar = c(0,0,0,0)) 668 | communities <- fastgreedy.community(graph_mutual) 669 | 670 | community_colors <- c("#D7F0C9", "#BCF4F6", "#F6DFBC")[membership(communities)] 671 | 672 | plot(communities, graph_mutual, 673 | vertex.label = ifelse( 674 | V(graph)$name==c("Derek"), V(graph)$name, NA), 675 | col = community_colors, 676 | vertex.label.family = "Arial", 677 | vertex.label.color = "#000000") 678 | ``` 679 | 680 | The fast greedy algorithm identifies 3 communities in our network, and this algorithm assigns Derek to the community of rather secluded, "less-popular" nodes (which we indicate in blue). 681 | 682 | Let's now try a different community-detection algorithm and see if we get similar results. We use the iterative edge-betweenness algorithm of Girvan and Newman (2002), another outdated approach that we again use only to illustrate the basic idea of community detection. The idea of this approach is that nodes that belong to separate communities (in our case, friendship groups) tend to be connected, in principle, through edges that have large values of edge betweenness, because these edges tend to be on shortest paths. 683 | 684 | ```{r} 685 | par(mar = c(0,0,0,0)) 686 | communities <- cluster_edge_betweenness(graph_mutual) 687 | 688 | community_colors <- c("#D7F0C9", "#BCF4F6", "#F6DFBC")[membership(communities)] 689 | 690 | plot(communities, graph_mutual, 691 | vertex.label = ifelse( 692 | V(graph)$name==c("Derek"), V(graph)$name, NA), 693 | col = community_colors, 694 | vertex.label.family = "Arial", 695 | vertex.label.color = "#000000") 696 | ``` 697 | 698 | This algorithm also identifies 3 communities, but now Derek is assigned to the largest community (a "popular crowd"), which we indicate in light blue. 699 | 700 | These examples again highlight the importance of being mindful of the decisions that we make, as different algorithms give different answers. Because a detailed review of the different algorithms for community detection (and a detailed review of other network calculations) is beyond the scope of this tutorial, we point to additional resources in Section 6. 701 | 702 | # Additional Resources 703 | 704 | In this section, we encourage interested researchers to seek additional resources on network analysis for more in-depth treatments of the concepts that we introduced in this tutorial. 705 | 706 | ## Additional Information on Community-Detection Algorithms used by igraph in R 707 | 708 | * *A Comparative Analysis of Community Detection Algorithms on Artificial Networks* by Yang, Algesheimer & Tessone (2016) compares the different community-detection algorithms that are used in igraph. The paper is available at https://www.nature.com/articles/srep30750. 709 | 710 | ## Analyzing and Visualizing Networks 711 | 712 | The present tutorial focused on igraph and a complementary package (visNetwork) for interactive visualization in R. 713 | 714 | Github repositories by the authors of these packages are available at the following websites. 715 | 716 | * *igraph*: https://github.com/igraph/igraph. 717 | 718 | * *visNetwork*: https://datastorm-open.github.io/visNetwork/. 719 | 720 | There are also many other tools that are available for researchers who are interested in analyzing and visualizing networks. We indicate a few of them here. 721 | 722 | * *R Programming* by Jared Lander consists of beginner-friendly video instructions for programming in R. Lesson 21 on Network Analysis may be of particular interest. It is available at http://shop.oreilly.com/product/0636920006119.do. 723 | 724 | * *Gephi* is an open-source GUI-based software for network visualization and exploration. For more information, see https://gephi.org/. 725 | 726 | * *NetworkX* is an open-source Python package for creating, manipulating, and studying the structure, dynamics, and functions of networks. For more information, see https://networkx.github.io/. 727 | 728 | * *Statnet* is another software package for network analysis and visualization. Interested users can download the Statnet package in R or use a graphical user interface in a web browser. For more information, including tutorials, see https://statnet.org/trac. 729 | 730 | * *MuxViz* is a tool for the visualization and analysis of multilayer networks; it uses a uses a graphical user interface. Additional information is available at https://github.com/manlius/muxViz. 731 | 732 | # Funding 733 | 734 | * This material is based upon work supported by the National Science Foundation under Grant No. 1835239 and SBE Postdoctoral Research Fellowship under Grant No. 1911783. 735 | 736 | * *Any opinions, findings, and conclusions or recommendations expressed in this material are those of the author(s) and do not necessarily reflect the views of the National Science Foundation.* -------------------------------------------------------------------------------- /adjacency_matrix.csv: -------------------------------------------------------------------------------- 1 | "","Alec","Amber","Amy","Anna","Bryce","Calvin","Cameron","Carly","Christopher","Claire","Colton","Damien","Destiny","Derek","Dylan","Elina","Elizabeth","Ian","Jeremy","Jessica","Joseph","Joshua","Karis","Kathryn","Klaudya","Koltan","Kolton","Kristjan","Madelyn","Marissa","Meagan","Mersaidi","Miley","Morgan","Natalie","Nazanine","Rebecca","Sara","Sarah","Stone","Suzette","Talliea","Taylor","Thomas","Traci","Treyvon","Tyler","Vancil","Zachary","Zoey" 2 | "Alec",0,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 3 | "Amber",1,0,1,1,1,1,1,1,1,1,1,1,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,0,0,0,0,0,0,0,0,0 4 | "Amy",1,1,0,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 5 | "Anna",1,1,1,0,1,1,1,1,1,1,1,1,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,0,0,0,0,0,0,0,0,0 6 | "Bryce",1,1,1,1,0,1,1,1,1,1,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 7 | "Calvin",1,1,1,1,1,0,1,1,1,1,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 8 | "Cameron",1,1,1,1,1,1,0,1,1,1,1,1,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,0,0,0,0,0,0,0,0,0 9 | "Carly",1,1,1,1,1,1,1,0,1,1,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 10 | "Christopher",1,1,1,1,1,1,1,1,0,1,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 11 | "Claire",1,1,1,1,1,1,1,1,1,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,0,0,0,0,0,0,0,0,0,0,0,0 12 | "Colton",1,1,1,1,1,1,1,1,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 13 | "Damien",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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 14 | "Destiny",0,0,0,0,0,0,0,0,0,0,0,0,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,0,1,1,1,1,1,1,1,1,1,1,1,1 15 | "Derek",0,0,0,0,1,1,0,1,0,1,1,0,1,0,1,0,0,0,1,0,1,0,1,0,0,1,1,0,1,1,0,0,0,0,0,0,0,0,1,0,1,0,1,0,0,0,0,0,0,0 16 | "Dylan",0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1,1,1,0,0,0,1,1,1,1,0,1,1,1,1,0,1,0,1,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0 17 | "Elina",0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,1,1,1,1,0,1,0,1,0,1,0,1,1,1,0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0,0 18 | "Elizabeth",0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,1,0,0,1,0,0,0,0,0,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0 19 | "Ian",0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,1,0,1,1,0,1,1,1,1,1,1,0,0,0,1,0,0,0,1,0,1,0,1,0,0,1,1,0,0,0,0,0 20 | "Jeremy",0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,1,0,0,0,0,1,0,0,1,0,0,0,0,0,0,1,1,0,0,1,0,0,1,1,0,1,0,1,0,0,0,0,0 21 | "Jessica",0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,1,0,0,1,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,1,0,1,1,1,0,0,0,0,0,0 22 | "Joseph",0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,1,1,0,0,0,0,0,0,1,1,0,1,0,0,0,1,1,1,1,0,1,0,0,0,0,0 23 | "Joshua",0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,1,0,0,0,0,0,1,0,0,1,0,0,0,0,1,1,1,0,0,1,0,0,1,0,1,1,1,0,0,0,0,0,0 24 | "Karis",0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1,1,0,0,1,1,0,0,0,0,0,1,0,0,1,0,1,1,0,1,0,1,1,0,1,1,0,1,1,0,0,0,0,0 25 | "Kathryn",0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,0,0,0,0,0,1,0,1,1,0,1,1,0,0,0,0,1,0,0,0,0,1,1,1,0,0,1,0,0,0,0,0,0 26 | "Klaudya",0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,0,0,1,1,0,0,1,0,1,0,1,0,0,0,1,1,1,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,0 27 | "Koltan",0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,1,0,0,0,0,1,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,0,1,1,1,0,1,1,0,0,0,0,0 28 | "Kolton",0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,1,0,0,1,1,0,0,0,1,1,0,1,0,1,0,1,0,0,0,1,1,0,1,1,0,0,0,0,0,0 29 | "Kristjan",0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,1,1,0,1,0,0,0,0,1,1,0,0,0,1,0,0,1,1,0,1,0,0,0,0,0,0,0 30 | "Madelyn",0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,1,0,1,1,0,0,0,1,0,1,1,0,1,1,0,0,1,0,1,0,1,1,0,1,1,0,0,0,0,0 31 | "Marissa",0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,1,1,1,0,0,1,1,0,1,1,0,0,0,1,1,0,0,0,0,1,1,0,0,0,1,0,1,1,0,0,0,0,0 32 | "Meagan",0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1,0,1,1,1,0,1,1,1,1,0,0,0,1,1,0,0,1,1,0,1,1,1,0,1,0,0,0,0,0,0 33 | "Mersaidi",0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,1,0,1,0,0,1,1,1,1,1,0,1,1,1,0,1,1,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0 34 | "Miley",0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,0,1,0,1,0,0,1,0,1,0,1,0,1,0,0,0,0,0 35 | "Morgan",0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,1,0,1,1,0,1,0,1,0,1,1,1,0,1,1,0,0,0,0,1,0,1,1,0,1,1,0,1,0,0,0,0,0 36 | "Natalie",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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 37 | "Nazanine",0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,1,0,0,1,1,1,1,0,0,1,0,1,0,1,1,0,1,0,0,0,1,1,0,1,0,0,1,1,0,0,0,0,0 38 | "Rebecca",0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,1,1,0,0,1,1,0,1,1,1,1,1,1,0,1,0,0,0,0,1,0,1,0,1,1,1,1,0,0,0,0,0 39 | "Sara",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,0,1,1,0,0,1,0,1,0,1,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,1,1,1,0,0 40 | "Sarah",0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,1,0,1,1,0,0,1,1,1,0,0,0,0,0,0,0,0 41 | "Stone",0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,0,1,1,0,1,0,0,0,0,0,1,1,1,0,0,0,0,0,0 42 | "Suzette",0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,0,1,0,1,0,1,0,0,1,0,1,0,1,0,0,1,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0 43 | "Talliea",0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,1,0,1,1,1,0,0,0,1,1,0,0,1,1,1,1,0,1,0,1,0,1,0,0,0,1,0,0,0,0,0,0 44 | "Taylor",0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,1,1,1,0,1,0,1,1,0,1,0,1,0,1,0,0,0,0,1,0,0,1,0,1,0,0,0,1,0,0,0,0,0 45 | "Thomas",0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,1,0,1,1,0,1,1,1,0,0,0,0,1,0,0,0,1,1,0,1,1,0,0,1,0,1,0,0,0,0,0 46 | "Traci",0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,0,0,0,1,1,1,1,1,0,0,1,1,1,1,0,1,1,0,1,1,1,0,0,1,0,0,0,0,0,0 47 | "Treyvon",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,1,0,0,0,0,0,0,0,0,1,1,1,1 48 | "Tyler",0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,1,0,1,0,0,1,0,1,1,1,0,0,1,0,0,0,0,1,1,0,1,0,0,1,0,1,0,1,1,1 49 | "Vancil",0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,1,0,1,1,1,0,0,0,0,1,1,1,1,0,0,0,1,0,1,0,0,0,1,0,1,1,0,1,1 50 | "Zachary",0,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,0,0,0,0,0,0,1,0,0,0,1,1,1,1,1,0,1,1,1,1,1,0,1 51 | "Zoey",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,1,0,0,0,0,0,1,1,1,0,0,0,0,1,1,1,1,1,0 52 | --------------------------------------------------------------------------------