Plotting social networks

1 Introduction

In this assignment/tutorial I will demonstrate how to plot networks with the igraph package. During the workgroup I will explain all code. For those of you who don’t attend the workgroups, google knows way more than I do.
Someone who also knows more than I do, especially with respect to plotting of Social Networks is Katya Ognyanova (aka Kateto). Please visit her site.

In the upper left and right corner of the code blocks you will find copy-to-clipboard buttons. Use these buttons to copy the code to your own editor.

2 Before you start

Before you start, check whether you run the latest RStudio version (from the Help menu, pick ‘check for updates’ and whether you need to update R.

install.packages("installr")  #you  first install packages
require(installr)  #then you will need to activate packages. 
updateR()  #run the function to start the update process

Give your script a nice name. Include the author, and data when you last modified the script. Include a lot of comments in your script! Don’t forget, always start with cleaning up your workspace.

### Author: JOCHEM TOLSMA### Lastmod: 31-08-2020###

# cleanup workspace
rm(list = ls())

And set your working directory.

# set working directory
setwd("C:\\YOURDIR\\YOURSUBDIR\\YOURSUBSUBDIR\\")  #change to your own workdirectory

Install the packages you will need.

# install packages
library(igraph)

3 Data

We are going to play with Twitter Networks among Dutch MPs.

Download twitter_20190919.Rdata

Load the Robject and have a look at it. Save the list elements in separate objects.

load("static/twitter_20190919.RData")  #change to your working directory
str(twitter_20190919, 1)
keyf <- twitter_20190919[[1]]
mydata <- twitter_20190919[[2]]
seats <- twitter_20190919[[3]]
## List of 3
##  $ keyf  :'data.frame':  147 obs. of  41 variables:
##  $ mydata:List of 8
##   ..- attr(*, "higher")= Named logi [1:9] FALSE FALSE FALSE FALSE FALSE FALSE ...
##   .. ..- attr(*, "names")= chr [1:9] "fnet,fnet" "atmnet,fnet" "rtnet,fnet" "fnet,atmnet" ...
##   ..- attr(*, "disjoint")= Named logi [1:9] FALSE FALSE FALSE FALSE FALSE FALSE ...
##   .. ..- attr(*, "names")= chr [1:9] "fnet,fnet" "atmnet,fnet" "rtnet,fnet" "fnet,atmnet" ...
##   ..- attr(*, "atLeastOne")= Named logi [1:9] FALSE FALSE FALSE FALSE FALSE FALSE ...
##   .. ..- attr(*, "names")= chr [1:9] "fnet,fnet" "atmnet,fnet" "rtnet,fnet" "fnet,atmnet" ...
##   ..- attr(*, "class")= chr "siena"
##  $ seats :'data.frame':  150 obs. of  5 variables:

So, what do we have?

  • keyf: a data.frame on 147 Dutch MPs.
  • mydata: This an object which is ready to analyze in RSiena. It is actually a quite complicated object. For now three things are important:
    1. The nodes in mydata are the same as in keyf and in seats.
    2. It contains the twitter data at three timepoints (in mydata$depvars). We have three layers:
    • fnet: who follows whom
    • atmnet: who atmentions whom
    • rtnet: who retweats whom
    1. It also contains timeinvariant information on the nodes (in mydata$cCovars)
  • seats: a dataset which contains the coordinates of the seats in the House of Parliament in the Netherlands.

We are going to focus on the atmentions of politicians. This is most closely related to political discussion. Thus who is having discussions with whom on Twitter?

Let us go fishing for some data:

fnet <- mydata$depvars$fnet
atmnet <- mydata$depvars$atmnet
rtnet <- mydata$depvars$rtnet

vrouw <- mydata$cCovars$vrouw
partij <- mydata$cCovars$partij
ethminz <- mydata$cCovars$ethminz
lft <- mydata$cCovars$lft

# if you construct an object for RSiena, covariates are mean centered by default. I would like to
# have the original values again.
ethminz <- ethminz + attributes(ethminz)$mean
partij <- partij + attributes(partij)$mean
vrouw <- vrouw + attributes(vrouw)$mean
lft <- lft + attributes(lft)$mean

Have a look at the network data. What are we a looking at?

str(fnet)
##  'sienaDependent' num [1:147, 1:147, 1:3] 0 0 0 1 0 1 0 1 1 1 ...
##  - attr(*, "type")= chr "oneMode"
##  - attr(*, "sparse")= logi FALSE
##  - attr(*, "nodeSet")= chr "Actors"
##  - attr(*, "netdims")= int [1:3] 147 147 3
##  - attr(*, "allowOnly")= logi TRUE
##  - attr(*, "uponly")= logi [1:2] TRUE FALSE
##  - attr(*, "downonly")= logi [1:2] FALSE FALSE
##  - attr(*, "distance")= int [1:2] 527 277
##  - attr(*, "vals")=List of 3
##   ..$ : 'table' int [1:4(1d)] 15781 5389 292 147
##   .. ..- attr(*, "dimnames")=List of 1
##   .. .. ..$ mymat: chr [1:4] "0" "1" "10" NA
##   ..$ : 'table' int [1:4(1d)] 15254 5916 292 147
##   .. ..- attr(*, "dimnames")=List of 1
##   .. .. ..$ mymat: chr [1:4] "0" "1" "10" NA
##   ..$ : 'table' int [1:3(1d)] 15457 6005 147
##   .. ..- attr(*, "dimnames")=List of 1
##   .. .. ..$ mymat: chr [1:3] "0" "1" NA
##  - attr(*, "nval")= int [1:3] 21462 21462 21462
##  - attr(*, "noMissing")= num [1:3] 0 0 0
##  - attr(*, "noMissingEither")= num [1:2] 0 0
##  - attr(*, "nonMissingEither")= num [1:2] 21462 21462
##  - attr(*, "balmean")= num 0.347
##  - attr(*, "structmean")= num 0.321
##  - attr(*, "simMean")= logi NA
##  - attr(*, "symmetric")= logi FALSE
##  - attr(*, "missing")= logi FALSE
##  - attr(*, "structural")= logi TRUE
##  - attr(*, "range2")= num [1:2] 0 1
##  - attr(*, "ones")= Named int [1:3] 5389 5916 6005
##   ..- attr(*, "names")= chr [1:3] "1" "1" "1"
##  - attr(*, "density")= Named num [1:3] 0.251 0.276 0.28
##   ..- attr(*, "names")= chr [1:3] "1" "1" "1"
##  - attr(*, "degree")= Named num [1:3] 36.7 40.2 40.9
##   ..- attr(*, "names")= chr [1:3] "1" "1" "1"
##  - attr(*, "averageOutDegree")= num 39.3
##  - attr(*, "averageInDegree")= num 39.3
##  - attr(*, "maxObsOutDegree")= num [1:3] 137 137 137
##  - attr(*, "missings")= num [1:3] 0 0 0
##  - attr(*, "name")= chr "fnet"
# It is just a 'sienaDependent' something [1:147,1:147,1:3]

fnet1 <- fnet[, , 1]
atmnet1 <- atmnet[, , 1]
atmnet2 <- atmnet[, , 2]
atmnet3 <- atmnet[, , 3]

It is just a ‘sienaDependent’ something [1:147,1:147,1:3] but with a lot of attributes which we may ignore for now. It is an array. In this array our nominations are stored in adjacency matrices. I selected the friendship relations and the atmention relations of the first wave.

You may wonder why we only have 147 nodes (of MPs) in our data. Well that is because at the time of writing three MPs did not have a twitter account or at least we could not find it.

One final thing before we can go and play with the data. We have to replace the missing values of RSiena 10 (structural zeros) into 0 (or NA) as well.

# table(fnet1, useNA='always') #uncomment if you want
fnet1[fnet1 == 10] <- 0
# table(fnet1, useNA='always') #uncomment if you want

atmnet1[atmnet1 == 10] <- 0
atmnet2[atmnet2 == 10] <- 0
atmnet3[atmnet3 == 10] <- 0

4 First plots

The first step is to make a ‘graph object’.

library(igraph)

G1 <- igraph::graph_from_adjacency_matrix(atmnet1, mode = "directed", weighted = NULL, diag = TRUE, add.colnames = NA, 
    add.rownames = NA)

Suppose you would like to add the data to this graph.

require(igraph)
# we need to retrieve the edges.
edges <- as_data_frame(G1, what = "edges")

# the first variable of the data we can attach needs to be some id, thus reorder columns of keyf
keyf <- cbind(keyf$EGOid, keyf[, names(keyf) != "EGOid"])
# the name has been changed as well. Lets correct this
names(keyf)[1] <- "EGOid"

# rebuild the graph.
G1 <- graph_from_data_frame(edges, directed = TRUE, vertices = keyf)

# I am a bit puzzled where the data is stored exactly but the same data as in keyf is now attached to
# the vertices.

# thus to find the names of our MPs we could now do this:
V(G1)$Naam
##   [1] "Agema, Fleur                            " "Amhaouch, Mustafa                       "
##   [3] "Arib, Khadija                           " "v. Ark, Tamara                          "
##   [5] "Azmani, Malik                           " "Beertema, Harm                          "
##   [7] "Belhaj, Salima                          " "Bergkamp, Vera                          "
##   [9] "Bisschop, Roelof                        " "Bosma, Martin                           "
##  [11] "Bosman, Andre                           " "ten Broeke, Han                         "
##  [13] "Bruins Slot, Hanke                      " "Van Dijk, Jasper                        "
##  [15] "Dijkgraaf, Elbert                       " "Dijkstra, Pia                           "
##  [17] "Dijkstra, Remco                         " "Dik-Faber, Carla                        "
##  [19] "Duisenberg, Pieter                      " "Geurts, Jaco                            "
##  [21] "De Graaf, Machiel                       " "Grashoff, Rik                           "
##  [23] "Graus, Dion                             " "Van Haersma Buma, Sybrand               "
##  [25] "Harbers, Mark                           " "Heerma, Pieter                          "
##  [27] "Helder, Lilian                          " "Van Helvert, Martijn                    "
##  [29] "Keijzer, Mona                           " "Klaver, jesse                           "
##  [31] "Knops, Raymond                          " "Kooiman, Nine                           "
##  [33] "Koolmees, Wouter                        " "Krol, Henk                              "
##  [35] "Kuiken, Attje                           " "Kuzu, Tunahan                           "
##  [37] "Leijten, Renske                         " "Lodders, Helma                          "
##  [39] "Madlener, Barry                         " "Van Meenen, Paul                        "
##  [41] "Mulder, Agnes                           " "Nijboer, Henk                           "
##  [43] "Nijkerken-de Haan, Chantal              " "Van Nispen, Michiel                     "
##  [45] "Omtzigt, Pieter                         " "Van Oosten, Foort                       "
##  [47] "Ozturk, Selcuk                          " "Pechtold, Alexander                     "
##  [49] "Van Raak, Ronald                        " "Roemer, Emile                           "
##  [51] "Rog, Michel                             " "Ronnes, Erik                            "
##  [53] "De Roon, Raymond                        " "Rutte, Arno                             "
##  [55] "Schouten, Carola                        " "Segers, Gert-Jan                        "
##  [57] "Sjoerdsma, Sjoerd                       " "Van der Staaij, Kees                    "
##  [59] "Tellegen, Ockje                         " "Thieme, Marianne                        "
##  [61] "Van Toorenburg, Madeleine               " "Van Veldhoven, Stientje                 "
##  [63] "Verhoeven, Kees                         " "Visser, Barbara                         "
##  [65] "Voordewind, Joel                        " "Voortman, Linda                         "
##  [67] "De Vries, Aukje                         " "Wassenberg, Frank                       "
##  [69] "Van Weyenberg, Steven                   " "Wilders, Geert                          "
##  [71] "Van t'Wout, Bas                         " "Ziengs, Erik                            "
##  [73] "Zijlstra, Halbe                         " "Rutte, Mark                             "
##  [75] "Ploumen, Lilianne                       " "Hennis-Plasschaert, Jeanine             "
##  [77] "Dijsselbloem, Jeroen                    " "Asscher, Lodewijk                       "
##  [79] "Dijksma, Sharon                         " "Dekker, Sander                          "
##  [81] "Dijkhoff, Klaas                         " "Thierry Baudet                          "
##  [83] "Eppo Bruins                             " "LILIAN MARIJNISSEN                      "
##  [85] "SADET KARABULUT                         " "SANDRA BECKERMAN                        "
##  [87] "PETER KWINT                             " "BART VAN KENT                           "
##  [89] "CEM LACIN                               " "FRANK FUTSELAAR                         "
##  [91] "MAARTEN HIJINK                          " "Ingrid van Engelshoven                  "
##  [93] "Jan Paternotte                          " "Rob Jetten                              "
##  [95] "Jessica van Eijs                        " "Maarten Groothuizen                     "
##  [97] "Rens Raemakers                          " "Achraf Bouali                           "
##  [99] "Antje Diertens                          " "Tjeerd de Groot                         "
## [101] "René Peters                             " "Harry van der Molen                     "
## [103] "Anne Kuik                               " "Chris van Dam                           "
## [105] "Joba van den Berg-Jansen                " "Maurits von Martels                     "
## [107] "Dennis Wiersma                          " "Bente Becker                            "
## [109] "Sophie Hermans                          " "Anne Mulder                             "
## [111] "Dilan Yesilgöz-Zegerius                 " "Daniel Koerhuis                         "
## [113] "Zohair el Yassini                       " "Martin Wörsdörfer                       "
## [115] "Arne Weverling                          " "Sven Koopmans                           "
## [117] "Jan Middendorp                          " "Léonie Sazias                           "
## [119] "Martin van Rooijen                      " "Corrie van Brenk                        "
## [121] "Esther Ouwehand                         " "Kathalijne Buitenweg                    "
## [123] "Tom van der Lee                         " "Corinne Ellemeet                        "
## [125] "Zihni Özdil                             " "Bart Snels                              "
## [127] "Suzanne Kröger                          " "Bram van Oijk                           "
## [129] "Nevin Özütok                            " "Lisa Westerveld                         "
## [131] "Isabelle Diks                           " "Liesbeth van Tongeren                   "
## [133] "Lammert van Raan                        " "Femke Merel Arissen                     "
## [135] "Farid Azarkan                           " "Gijs van Dijk                           "
## [137] "Kirsten van den Hul                     " "Gerbrands, Karen                        "
## [139] "Theo Hiddema                            " "Vicky Maeijer                           "
## [141] "Gidi Markuszower                        " "Danai van Weerdenburg                   "
## [143] "Edgar Mulder                            " "Léon de Jong                            "
## [145] "Gabriëlle Popken                        " "Alexander Kops                          "
## [147] "Roy van Aalst                           "

But now let us start plotting.

plot(G1)