You may cite these labs as follows: McFarland, Daniel, Solomon Messing, Mike Nowak, and Sean Westwood. 2010. “Social Network Analysis
Labs in R.” Stanford University.

LAB 1 - Introductory Lab
The point of this lab is to introduce students to the packages of
SNA and Igraph, to cover some basic R commands, to load and manage
data, to generate graph visualizations, and to export the data for use elsewhere.

0. R BASICS

Any line starting with # is a “comment” line and is ignored by R. Any other line is treated as a command. Run commands by copying and pasting them into the R Console. If (when) you get confused, a good place to start is with R’s built-in help functionality. R offers detailed help files for each function and each package. To access help type ?[function or package name] in the console. For example, for help on the “sum” function, type:

?sum
## starting httpd help server ... done

To install all packages need for Social Network Analysis Labs in R, uncomment and run the following code:

source(“http://sna.stanford.edu/setup.R”)

You only need to run this once per computer!

To load the packages from , you need to call the “library” command. Note that you need to do this each session; packages don’t load automatically by default (though you can set this as a preference if you’d like).

For this lab, we will use the “igraph” package. A manual is available at http://cran.r-project.org/web/packages/igraph/igraph.pdf.

library(igraph)
## Warning: package 'igraph' was built under R version 3.4.3
## 
## Attaching package: 'igraph'
## The following objects are masked from 'package:stats':
## 
##     decompose, spectrum
## The following object is masked from 'package:base':
## 
##     union

Sometimes, different packages overlap in functionality and cause unexpected behavior when both are loaded simultaneously. If you ever want to remove an existing library, use the “detach” command:

detach(package:igraph)

IMPORTANT NOTE: Unlike in most languages, R objects are numbered from 1 instead of 0, so if you want the first element in a vector, you would reference it by vector_name[1]. HOWEVER, igraph objects are numbered starting from 0. This can lead to lots of confusion, since it’s not always obvious at first which objects are native to R and which belong to igraph.

1. LOADING DATA

The <- operator sets a variable equal to something. In this case, we will set a number of basic R data structures, called “data frames,” to hold the contents of the files we will open.

read.table() is the most common R command for loading data from files in which values are in tabular format. The function loads the table into a data frame object, which is the basic data type for most operations in R. By default, R assumes that the table has no header and is delimited by any white space; these settings are fine for our purposes here.

One handy aspect of R is that you can read in data from a URL directly by referencing the URL in the read.table() function, as follows:

advice_data_frame <- read.table('http://sna.stanford.edu/sna_R_labs/data/Krack-High-Tec-edgelist-Advice.txt')
friendship_data_frame <- read.table('http://sna.stanford.edu/sna_R_labs/data/Krack-High-Tec-edgelist-Friendship.txt')
reports_to_data_frame <- read.table('http://sna.stanford.edu/sna_R_labs/data/Krack-High-Tec-edgelist-ReportsTo.txt')

If the files you want to work with are on your local machine, the easiest way to access them is to first set your working directory via the setwd() command, and then reference the files by name:

setwd("C:/Users/rjeev/Desktop/WS1") 

setwd(‘path/to/your_directory’) your_data_frame <- read.table(‘your_file_name’)

Note that when you set a variable equal to something, if all goes well R will not provide any feedback. To see the data we just loaded, it’s necessary to call the variables directly.

advice_data_frame
##     V1 V2 V3
## 1    1  1  0
## 2    1  2  1
## 3    1  3  0
## 4    1  4  1
## 5    1  5  0
## 6    1  6  0
## 7    1  7  0
## 8    1  8  1
## 9    1  9  0
## 10   1 10  0
## 11   1 11  0
## 12   1 12  0
## 13   1 13  0
## 14   1 14  0
## 15   1 15  0
## 16   1 16  1
## 17   1 17  0
## 18   1 18  1
## 19   1 19  0
## 20   1 20  0
## 21   1 21  1
## 22   2  1  0
## 23   2  2  0
## 24   2  3  0
## 25   2  4  0
## 26   2  5  0
## 27   2  6  1
## 28   2  7  1
## 29   2  8  0
## 30   2  9  0
## 31   2 10  0
## 32   2 11  0
## 33   2 12  0
## 34   2 13  0
## 35   2 14  0
## 36   2 15  0
## 37   2 16  0
## 38   2 17  0
## 39   2 18  0
## 40   2 19  0
## 41   2 20  0
## 42   2 21  1
## 43   3  1  1
## 44   3  2  1
## 45   3  3  0
## 46   3  4  1
## 47   3  5  0
## 48   3  6  1
## 49   3  7  1
## 50   3  8  1
## 51   3  9  1
## 52   3 10  1
## 53   3 11  1
## 54   3 12  1
## 55   3 13  0
## 56   3 14  1
## 57   3 15  0
## 58   3 16  0
## 59   3 17  1
## 60   3 18  1
## 61   3 19  0
## 62   3 20  1
## 63   3 21  1
## 64   4  1  1
## 65   4  2  1
## 66   4  3  0
## 67   4  4  0
## 68   4  5  0
## 69   4  6  1
## 70   4  7  0
## 71   4  8  1
## 72   4  9  0
## 73   4 10  1
## 74   4 11  1
## 75   4 12  1
## 76   4 13  0
## 77   4 14  0
## 78   4 15  0
## 79   4 16  1
## 80   4 17  1
## 81   4 18  1
## 82   4 19  0
## 83   4 20  1
## 84   4 21  1
## 85   5  1  1
## 86   5  2  1
## 87   5  3  0
## 88   5  4  0
## 89   5  5  0
## 90   5  6  1
## 91   5  7  1
## 92   5  8  1
## 93   5  9  0
## 94   5 10  1
## 95   5 11  1
## 96   5 12  0
## 97   5 13  1
## 98   5 14  1
## 99   5 15  0
## 100  5 16  1
## 101  5 17  1
## 102  5 18  1
## 103  5 19  1
## 104  5 20  1
## 105  5 21  1
## 106  6  1  0
## 107  6  2  0
## 108  6  3  0
## 109  6  4  0
## 110  6  5  0
## 111  6  6  0
## 112  6  7  0
## 113  6  8  0
## 114  6  9  0
## 115  6 10  0
## 116  6 11  0
## 117  6 12  0
## 118  6 13  0
## 119  6 14  0
## 120  6 15  0
## 121  6 16  0
## 122  6 17  0
## 123  6 18  0
## 124  6 19  0
## 125  6 20  0
## 126  6 21  1
## 127  7  1  0
## 128  7  2  1
## 129  7  3  0
## 130  7  4  0
## 131  7  5  0
## 132  7  6  1
## 133  7  7  0
## 134  7  8  0
## 135  7  9  0
## 136  7 10  0
## 137  7 11  1
## 138  7 12  1
## 139  7 13  0
## 140  7 14  1
## 141  7 15  0
## 142  7 16  0
## 143  7 17  1
## 144  7 18  1
## 145  7 19  0
## 146  7 20  0
## 147  7 21  1
## 148  8  1  0
## 149  8  2  1
## 150  8  3  0
## 151  8  4  1
## 152  8  5  0
## 153  8  6  1
## 154  8  7  1
## 155  8  8  0
## 156  8  9  0
## 157  8 10  1
## 158  8 11  1
## 159  8 12  0
## 160  8 13  0
## 161  8 14  0
## 162  8 15  0
## 163  8 16  0
## 164  8 17  0
## 165  8 18  1
## 166  8 19  0
## 167  8 20  0
## 168  8 21  1
## 169  9  1  1
## 170  9  2  1
## 171  9  3  0
## 172  9  4  0
## 173  9  5  0
## 174  9  6  1
## 175  9  7  1
## 176  9  8  1
## 177  9  9  0
## 178  9 10  1
## 179  9 11  1
## 180  9 12  1
## 181  9 13  0
## 182  9 14  1
## 183  9 15  0
## 184  9 16  1
## 185  9 17  1
## 186  9 18  1
## 187  9 19  0
## 188  9 20  0
## 189  9 21  1
## 190 10  1  1
## 191 10  2  1
## 192 10  3  1
## 193 10  4  1
## 194 10  5  1
## 195 10  6  0
## 196 10  7  0
## 197 10  8  1
## 198 10  9  0
## 199 10 10  0
## 200 10 11  1
## 201 10 12  0
## 202 10 13  1
## 203 10 14  0
## 204 10 15  1
## 205 10 16  1
## 206 10 17  1
## 207 10 18  1
## 208 10 19  1
## 209 10 20  1
## 210 10 21  0
## 211 11  1  1
## 212 11  2  1
## 213 11  3  0
## 214 11  4  0
## 215 11  5  0
## 216 11  6  0
## 217 11  7  1
## 218 11  8  0
## 219 11  9  0
## 220 11 10  0
## 221 11 11  0
## 222 11 12  0
## 223 11 13  0
## 224 11 14  0
## 225 11 15  0
## 226 11 16  0
## 227 11 17  0
## 228 11 18  0
## 229 11 19  0
## 230 11 20  0
## 231 11 21  0
## 232 12  1  0
## 233 12  2  0
## 234 12  3  0
## 235 12  4  0
## 236 12  5  0
## 237 12  6  0
## 238 12  7  1
## 239 12  8  0
## 240 12  9  0
## 241 12 10  0
## 242 12 11  0
## 243 12 12  0
## 244 12 13  0
## 245 12 14  0
## 246 12 15  0
## 247 12 16  0
## 248 12 17  0
## 249 12 18  0
## 250 12 19  0
## 251 12 20  0
## 252 12 21  1
## 253 13  1  1
## 254 13  2  1
## 255 13  3  0
## 256 13  4  0
## 257 13  5  1
## 258 13  6  0
## 259 13  7  0
## 260 13  8  0
## 261 13  9  1
## 262 13 10  0
## 263 13 11  0
## 264 13 12  0
## 265 13 13  0
## 266 13 14  1
## 267 13 15  0
## 268 13 16  0
## 269 13 17  0
## 270 13 18  1
## 271 13 19  0
## 272 13 20  0
## 273 13 21  0
## 274 14  1  0
## 275 14  2  1
## 276 14  3  0
## 277 14  4  0
## 278 14  5  0
## 279 14  6  0
## 280 14  7  1
## 281 14  8  0
## 282 14  9  0
## 283 14 10  0
## 284 14 11  0
## 285 14 12  0
## 286 14 13  0
## 287 14 14  0
## 288 14 15  0
## 289 14 16  0
## 290 14 17  0
## 291 14 18  1
## 292 14 19  0
## 293 14 20  0
## 294 14 21  1
## 295 15  1  1
## 296 15  2  1
## 297 15  3  1
## 298 15  4  1
## 299 15  5  1
## 300 15  6  1
## 301 15  7  1
## 302 15  8  1
## 303 15  9  1
## 304 15 10  1
## 305 15 11  1
## 306 15 12  1
## 307 15 13  1
## 308 15 14  1
## 309 15 15  0
## 310 15 16  1
## 311 15 17  1
## 312 15 18  1
## 313 15 19  1
## 314 15 20  1
## 315 15 21  1
## 316 16  1  1
## 317 16  2  1
## 318 16  3  0
## 319 16  4  0
## 320 16  5  0
## 321 16  6  0
## 322 16  7  0
## 323 16  8  0
## 324 16  9  0
## 325 16 10  1
## 326 16 11  0
## 327 16 12  0
## 328 16 13  0
## 329 16 14  0
## 330 16 15  0
## 331 16 16  0
## 332 16 17  0
## 333 16 18  1
## 334 16 19  0
## 335 16 20  0
## 336 16 21  0
## 337 17  1  1
## 338 17  2  1
## 339 17  3  0
## 340 17  4  1
## 341 17  5  0
## 342 17  6  0
## 343 17  7  1
## 344 17  8  0
## 345 17  9  0
## 346 17 10  0
## 347 17 11  0
## 348 17 12  0
## 349 17 13  0
## 350 17 14  0
## 351 17 15  0
## 352 17 16  0
## 353 17 17  0
## 354 17 18  0
## 355 17 19  0
## 356 17 20  0
## 357 17 21  1
## 358 18  1  1
## 359 18  2  1
## 360 18  3  1
## 361 18  4  1
## 362 18  5  1
## 363 18  6  0
## 364 18  7  1
## 365 18  8  1
## 366 18  9  1
## 367 18 10  1
## 368 18 11  1
## 369 18 12  0
## 370 18 13  1
## 371 18 14  1
## 372 18 15  1
## 373 18 16  1
## 374 18 17  0
## 375 18 18  0
## 376 18 19  1
## 377 18 20  1
## 378 18 21  1
## 379 19  1  1
## 380 19  2  1
## 381 19  3  1
## 382 19  4  0
## 383 19  5  1
## 384 19  6  0
## 385 19  7  1
## 386 19  8  0
## 387 19  9  0
## 388 19 10  1
## 389 19 11  1
## 390 19 12  0
## 391 19 13  0
## 392 19 14  1
## 393 19 15  1
## 394 19 16  0
## 395 19 17  0
## 396 19 18  1
## 397 19 19  0
## 398 19 20  1
## 399 19 21  0
## 400 20  1  1
## 401 20  2  1
## 402 20  3  0
## 403 20  4  0
## 404 20  5  0
## 405 20  6  1
## 406 20  7  0
## 407 20  8  1
## 408 20  9  0
## 409 20 10  0
## 410 20 11  1
## 411 20 12  1
## 412 20 13  0
## 413 20 14  1
## 414 20 15  1
## 415 20 16  1
## 416 20 17  1
## 417 20 18  1
## 418 20 19  0
## 419 20 20  0
## 420 20 21  1
## 421 21  1  0
## 422 21  2  1
## 423 21  3  1
## 424 21  4  1
## 425 21  5  0
## 426 21  6  1
## 427 21  7  1
## 428 21  8  1
## 429 21  9  0
## 430 21 10  0
## 431 21 11  0
## 432 21 12  1
## 433 21 13  0
## 434 21 14  1
## 435 21 15  0
## 436 21 16  0
## 437 21 17  1
## 438 21 18  1
## 439 21 19  0
## 440 21 20  1
## 441 21 21  0

Since this is a bit long, we can see just the top six rows via head()…

head(friendship_data_frame)
##   V1 V2 V3
## 1  1  1  0
## 2  1  2  1
## 3  1  3  0
## 4  1  4  1
## 5  1  5  0
## 6  1  6  0

or the bottom six rows via tail().

tail(reports_to_data_frame)
##     V1 V2 V3
## 436 21 16  0
## 437 21 17  0
## 438 21 18  0
## 439 21 19  0
## 440 21 20  0
## 441 21 21  0

To view your data in a spreadsheet-like window, use the command ‘fix()’.

fix(reports_to_data_frame)

The attribute data for this lab is in a comma-separated-value (CSV) file. read.csv() loads a CSV file into a data frame object. In this case, we do have a header row, so we set header=T, which tells R that the first row of data contains column names.

attributes <- read.csv('http://sna.stanford.edu/sna_R_labs/data/Krack-High-Tec-Attributes.csv', header=T)
attributes
##    AGE TENURE LEVEL DEPT
## 1   33  9.333     3    4
## 2   42 19.583     2    4
## 3   40 12.750     3    2
## 4   33  7.500     3    4
## 5   32  3.333     3    2
## 6   59 28.000     3    1
## 7   55 30.000     1    0
## 8   34 11.333     3    1
## 9   62  5.417     3    2
## 10  37  9.250     3    3
## 11  46 27.000     3    3
## 12  34  8.917     3    1
## 13  48  0.250     3    2
## 14  43 10.417     2    2
## 15  40  8.417     3    2
## 16  27  4.667     3    4
## 17  30 12.417     3    1
## 18  33  9.083     2    3
## 19  32  4.833     3    2
## 20  38 11.667     3    2
## 21  36 12.500     2    1

Other commands may be used to load data from files in different formats. read.delim() is a general function for loading any delimited text file. The default is tab-delimited, but this can be overridden by setting the “sep” parameter. For example:

f <- read.delim(“tab_delimited_file.txt”) f <- read.delim(“colon_delimited_file.txt”, sep=‘:’)

The ‘foreign’ package will allow you to read a few other custom data types, such as SPSS files via read.spss() and STATA files via read.dta().

When data files are part of an R package you can read them as follows:

data(kracknets, package = “NetData”)

In the future, we will load data this way. However, it is useful to get a sense of how things often must be done in R.

2.2. LOADING GRAPHS

For convenience, we can assign column names to our newly imported data frames. c() is a common generic R function that combines its arguments into a single vector.

colnames(advice_data_frame) <- c('ego', 'alter', 'advice_tie')
head(advice_data_frame)
##   ego alter advice_tie
## 1   1     1          0
## 2   1     2          1
## 3   1     3          0
## 4   1     4          1
## 5   1     5          0
## 6   1     6          0
colnames(friendship_data_frame) <- c('ego', 'alter', 'friendship_tie')
head(friendship_data_frame)
##   ego alter friendship_tie
## 1   1     1              0
## 2   1     2              1
## 3   1     3              0
## 4   1     4              1
## 5   1     5              0
## 6   1     6              0
colnames(reports_to_data_frame) <- c('ego', 'alter', 'reports_to_tie')
head(reports_to_data_frame)
##   ego alter reports_to_tie
## 1   1     1              0
## 2   1     2              1
## 3   1     3              0
## 4   1     4              0
## 5   1     5              0
## 6   1     6              0

Take a look at each data frame using the ’fix()" function. Note that you’ll need to close each fix window before R will evaluate the next line of code.

fix(advice_data_frame)
fix(friendship_data_frame)
fix(reports_to_data_frame)

Before we merge these data, we need to make sure ‘ego’ and ‘alter’ are the same across data sets. We can compare each row using the == syntax. The command below should return TRUE for every row if all ego rows are the same for advice and friendship:

advice_data_frame$ego == friendship_data_frame$ego
##   [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
##  [15] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
##  [29] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
##  [43] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
##  [57] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
##  [71] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
##  [85] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
##  [99] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [113] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [127] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [141] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [155] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [169] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [183] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [197] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [211] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [225] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [239] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [253] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [267] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [281] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [295] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [309] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [323] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [337] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [351] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [365] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [379] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [393] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [407] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [421] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
## [435] TRUE TRUE TRUE TRUE TRUE TRUE TRUE

That’s a lot of output to sort through. Instead, we can just have R return which row entries are not equal using the syntax below:

which(advice_data_frame$ego != friendship_data_frame$ego)
## integer(0)

Repeat for other variables

which(advice_data_frame$alter != friendship_data_frame$alter)
## integer(0)
which(reports_to_data_frame$alter != friendship_data_frame$alter)
## integer(0)
which(reports_to_data_frame$ego != friendship_data_frame$ego)
## integer(0)

Now that we’ve verified they are all the same, we can combine them into a single data frame.

krack_full_data_frame <- cbind(advice_data_frame, 
                               friendship_data_frame$friendship_tie, 
                               reports_to_data_frame$reports_to_tie)
head(krack_full_data_frame)
##   ego alter advice_tie friendship_data_frame$friendship_tie
## 1   1     1          0                                    0
## 2   1     2          1                                    1
## 3   1     3          0                                    0
## 4   1     4          1                                    1
## 5   1     5          0                                    0
## 6   1     6          0                                    0
##   reports_to_data_frame$reports_to_tie
## 1                                    0
## 2                                    1
## 3                                    0
## 4                                    0
## 5                                    0
## 6                                    0

Notice that the last two variable names are now “reports_to_data_frame\(reports_to_tie" and "friendship_data_frame\)friendship_tie”. That’s a little long. We can rename them as follows:

names(krack_full_data_frame)[4:5] <- c("friendship_tie", 
                                       "reports_to_tie")  
head(krack_full_data_frame)
##   ego alter advice_tie friendship_tie reports_to_tie
## 1   1     1          0              0              0
## 2   1     2          1              1              1
## 3   1     3          0              0              0
## 4   1     4          1              1              0
## 5   1     5          0              0              0
## 6   1     6          0              0              0

Another way to build the data frame is to use R’s data.frame syntax from the start:

krack_full_data_frame <- data.frame(ego = advice_data_frame[,1],
                                    alter = advice_data_frame[,2],
                                    advice_tie = advice_data_frame[,3],
                                    friendship_tie = friendship_data_frame[,3], 
                                    reports_to_tie = reports_to_data_frame[,3])
head(krack_full_data_frame)
##   ego alter advice_tie friendship_tie reports_to_tie
## 1   1     1          0              0              0
## 2   1     2          1              1              1
## 3   1     3          0              0              0
## 4   1     4          1              1              0
## 5   1     5          0              0              0
## 6   1     6          0              0              0

Now let’s move on to some data processing.

Reduce to non-zero edges so that the edge list only contains actual ties of some type.

krack_full_nonzero_edges <- subset(krack_full_data_frame, 
                                   (advice_tie > 0 | friendship_tie > 0 | reports_to_tie > 0))
head(krack_full_nonzero_edges)
##    ego alter advice_tie friendship_tie reports_to_tie
## 2    1     2          1              1              1
## 4    1     4          1              1              0
## 8    1     8          1              1              0
## 12   1    12          0              1              0
## 16   1    16          1              1              0
## 18   1    18          1              0              0

Now we can import our data into a “graph” object using igraph’s graph.data.frame() function. Coercing the data into a graph object is what allows us to perform network-analysis techniques.

krack_full <- graph.data.frame(krack_full_nonzero_edges) 
summary(krack_full)
## IGRAPH db4757a DN-- 21 232 -- 
## + attr: name (v/c), advice_tie (e/n), friendship_tie (e/n),
## | reports_to_tie (e/n)

By default, graph.data.frame() treats the first two columns of a data frame as an edge list and any remaining columns as edge attributes. Thus, the 232 edges appearing in the summary() output refer to the 232 pairs of vertices that are joined by any type of tie. The tie types themselves are listed as edge attributes.

To get a vector of edges for a specific type of tie, use the get.edge.attribute() function.

get.edge.attribute(krack_full, 'advice_tie')
##   [1] 1 1 1 0 1 1 1 0 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1
##  [36] 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 1 1 1 1 1 1 1
##  [71] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 0 1 1 1 1 1
## [106] 1 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 1 1 1 1 1 0 1 1 1 1 0 1 1 1 1
## [141] 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 0 0 1 0 0 0 0 0 0
## [176] 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 1 0 1 1 1 1 1
## [211] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
get.edge.attribute(krack_full, 'friendship_tie')
##   [1] 1 1 1 1 1 0 0 1 0 0 1 1 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 1 1 0 1 0 0 1
##  [36] 1 1 0 0 0 0 1 0 0 0 1 0 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0
##  [71] 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 1 1 0 1 0 0 1 0 0
## [106] 0 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 0 1 1 0 0 1 0 1 0 0 0 1 1 0 0 1 0
## [141] 1 0 1 1 0 0 1 0 1 0 0 1 0 0 0 1 0 0 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1
## [176] 1 1 1 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0 0 1 1 1 1 0 1 0
## [211] 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 1 0 1 1 0
get.edge.attribute(krack_full, 'reports_to_tie')
##   [1] 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0
##  [36] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0
##  [71] 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
## [106] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 1 0 0 0 0 0
## [141] 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
## [176] 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
## [211] 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0

If you would like to symmetrize the network, making all asymmetric ties symmetric, use the as.undirected() function:

krack_full_symmetrized <- as.undirected(krack_full, mode='collapse')
summary(krack_full_symmetrized)
## IGRAPH db5d54c UN-- 21 159 -- 
## + attr: name (v/c)

3. ADDING VERTEX ATTRIBUTES TO A GRAPH OBJECT

One way to add the attributes to your graph object is to iterate through each attribute and each vertex. This means that we will add one attribute at a time to each vertex in the network.

V(krack_full) returns a list of the IDs of each vertex in the graph. names(attributes) returns a list of the column names in the attributes table. The double-for loop tells R to repeat the code between the brackets once for each attribute and once for each vertex.

for (i in V(krack_full)) {
  for (j in names(attributes)) {
    krack_full <- set.vertex.attribute(krack_full, 
                                       j, 
                                       index = i, 
                                       attributes[i + 1, j])
  }
}

A shorter way is to just read in attribute names when you create the graph object:

First create a vector of vertex labels, in this case 1:n

attributes = cbind(1:length(attributes[,1]), attributes)
krack_full <- graph.data.frame(d = krack_full_nonzero_edges, 
                               vertices = attributes) 

Note that we now have ‘AGE,’ ‘TENURE,’ ‘LEVEL,’ and ‘DEPT’ listed alongside ‘name’ as vertex attributes.

summary(krack_full)
## IGRAPH db73cac DN-- 21 232 -- 
## + attr: name (v/c), AGE (v/n), TENURE (v/n), LEVEL (v/n), DEPT
## | (v/n), advice_tie (e/n), friendship_tie (e/n), reports_to_tie
## | (e/n)

We can see a list of the values for a given attribute for all of the actors in the network.

get.vertex.attribute(krack_full, 'AGE')
##  [1] 33 42 40 33 32 59 55 34 62 37 46 34 48 43 40 27 30 33 32 38 36
get.vertex.attribute(krack_full, 'TENURE')
##  [1]  9.333 19.583 12.750  7.500  3.333 28.000 30.000 11.333  5.417  9.250
## [11] 27.000  8.917  0.250 10.417  8.417  4.667 12.417  9.083  4.833 11.667
## [21] 12.500
get.vertex.attribute(krack_full, 'LEVEL')
##  [1] 3 2 3 3 3 3 1 3 3 3 3 3 3 2 3 3 3 2 3 3 2
get.vertex.attribute(krack_full, 'DEPT')
##  [1] 4 4 2 4 2 1 0 1 2 3 3 1 2 2 2 4 1 3 2 2 1

4. VISUALIZE THE NETWORKS

We can use R’s general-purpose plot() method to generate custom visualizations of the network.

R only lets us look at one plot at a time. To make our work easier we will save our plots as PDF files. To jus create a plot execute the code between the PDF function and “dev.off()”.

In order to save PDF files we must tell R where to put them. We do this with the setwd() command. You must put the full path to the folder where you will output the files here.

In OS X you can get this information by selecting the folder, right clicking and selecting “Get Info.” The path is listed under “Where.”

In Windows you can get this information by selecting the folder, right clicking and selecting “Properties.” The path information is listed “location”.

example: setwd(“/Users/seanwestwood/Desktop/lab_1”)

setwd("/Users/rjeev/Desktop/WS1")

First, let’s plot the network with all possible ties.

plot(krack_full)

This is a bit of a jumble, so let’s look at the networks for single edge types.

advice only

krack_advice_only <- delete.edges(krack_full, 
                                  E(krack_full)[get.edge.attribute(krack_full,
                                                                   name = "advice_tie") == 0])
summary(krack_advice_only)
## IGRAPH dba4778 DN-- 21 190 -- 
## + attr: name (v/c), AGE (v/n), TENURE (v/n), LEVEL (v/n), DEPT
## | (v/n), advice_tie (e/n), friendship_tie (e/n), reports_to_tie
## | (e/n)
plot(krack_advice_only)

Friendship only

krack_friendship_only <- delete.edges(krack_full, 
                                      E(krack_full)[get.edge.attribute(krack_full, 
                                                                       name = "friendship_tie") == 0])
summary(krack_friendship_only)
## IGRAPH dc14e86 DN-- 21 102 -- 
## + attr: name (v/c), AGE (v/n), TENURE (v/n), LEVEL (v/n), DEPT
## | (v/n), advice_tie (e/n), friendship_tie (e/n), reports_to_tie
## | (e/n)
plot(krack_friendship_only)

reports-to only

krack_reports_to_only <- delete.edges(krack_full, 
                                      E(krack_full)[get.edge.attribute(krack_full, 
                                                                       name = "reports_to_tie") == 0])
summary(krack_reports_to_only)
## IGRAPH dc29473 DN-- 21 20 -- 
## + attr: name (v/c), AGE (v/n), TENURE (v/n), LEVEL (v/n), DEPT
## | (v/n), advice_tie (e/n), friendship_tie (e/n), reports_to_tie
## | (e/n)
pdf("1.4_Krackhardt_Reports.pdf")
plot(krack_reports_to_only)

Still kind of messy, so let’s clean things up a bit. For simplicity, we’ll focus on reports_to ties for now.

First, we can optimize the layout by applying the layout algorithm to the specific set of ties we care about. Here we’ll use Fruchterman-Rheingold; other options are described in the igraph help page for “layout,” which can be accessed by entering ?layout.

reports_to_layout <- layout.fruchterman.reingold(krack_reports_to_only)
plot(krack_reports_to_only, 
     layout=reports_to_layout)

Now let’s color-code vertices by department and clean up the plot by removing vertex labels and shrinking the arrow size.

dept_vertex_colors = get.vertex.attribute(krack_full,"DEPT")
colors = c('Black', 'Red', 'Blue', 'Yellow', 'Green')
dept_vertex_colors[dept_vertex_colors == 0] = colors[1]
dept_vertex_colors[dept_vertex_colors == 1] = colors[2]
dept_vertex_colors[dept_vertex_colors == 2] = colors[3]
dept_vertex_colors[dept_vertex_colors == 3] = colors[4] 
dept_vertex_colors[dept_vertex_colors == 4] = colors[5]


plot(krack_reports_to_only, 
     layout=reports_to_layout, 
     vertex.color=dept_vertex_colors, 
     vertex.label=NA, 
     edge.arrow.size=.5)

Now let’s set the vertex size by tenure.

tenure_vertex_sizes = get.vertex.attribute(krack_full,"TENURE")

 
plot(krack_reports_to_only, 
     layout=reports_to_layout, 
     vertex.color=dept_vertex_colors, 
     vertex.label=NA, 
     edge.arrow.size=.5,
     vertex.size=tenure_vertex_sizes)

Now let’s incorporate additional tie types. We’ll use the layout generated by the reports-to ties but overlay the advice and friendship ties in red and blue.

tie_type_colors = c(rgb(1,0,0,.5), rgb(0,0,1,.5), rgb(0,0,0,.5))
E(krack_full)$color[ E(krack_full)$advice_tie==1 ] = tie_type_colors[1]
E(krack_full)$color[ E(krack_full)$friendship_tie==1 ] = tie_type_colors[2]
E(krack_full)$color[ E(krack_full)$reports_to_tie==1 ] = tie_type_colors[3]
E(krack_full)$arrow.size=.5 
V(krack_full)$color = dept_vertex_colors
V(krack_full)$frame = dept_vertex_colors

plot(krack_full, 
     layout=reports_to_layout, 
     vertex.color=dept_vertex_colors, 
     vertex.label=NA, 
     edge.arrow.size=.5,
     vertex.size=tenure_vertex_sizes)

Another option for visualizing different network ties relative to one another is to overlay the edges from one tie type on the structure generated by another tie type. Here we can use the reports-to layout but show the friendship ties:

plot(krack_friendship_only, 
     layout=reports_to_layout, 
     vertex.color=dept_vertex_colors, 
     vertex.label=NA, 
     edge.arrow.size=.5,
     vertex.size=tenure_vertex_sizes, 
     main='Krackhardt High-Tech Managers')

5. EXPORT THE NETWORK

The write.graph() function exports a graph object in various formats readable by other programs. There is no explicit option for a UCINET data type, but you can export the graph as a Pajek object by setting the ‘format’ parameter to ‘pajek.’ Note that the file will appear in whichever directory is set as the default in R’s preferences, unless you previously changed this via setwd().

write.graph(krack_full, file='krack_full.dl', format="pajek")

For a more general file type (e.g., importable to Excel), use the “edgelist” format. Note that neither of these will write the attributes; only the ties are maintained.

write.graph(krack_full, file='krack_full.txt', format="edgelist")