library(igraph)
A more advanced analysis of two-mode networks is to apply the theory/logic of Breiger’s (1974) duality of persons and groups.
The logic is that if people mutually affiliate with a group then they are symbolically connected.
The strength of that symbolic connection could be operationalised in terms of how many groups they are mutually affiliated to.
To do this we use a tool called bipartite projection. Bipartite projection is another word for two-mode.
First, though, we have to bring in our two mode network. Your two mode network may be stored as an adjacency matrix or an edgelist. The following chunk brings in a two mode network stored as an adjacency matrix.
hp <- read.csv(file.choose(),header=TRUE,row.names=1) ## select harry_potter_two_mode.csv
hp_mat <- as.matrix(hp)
head(hp_mat)
## Phoenix Dumbeldore.s.Army Death.Eaters Inquisitorial.Squad
## Albus Dumbledore 1 0 0 0
## Remus Lupin 1 0 0 0
## Molly Weasley 1 0 0 0
## Siruis Black 1 0 0 0
## Severus Snape 1 0 1 0
## Alastor Moody 1 0 0 0
## Prefect Gryffindor Ravenclaw Hufflepuff Slytherin
## Albus Dumbledore 1 1 0 0 0
## Remus Lupin 1 1 0 0 0
## Molly Weasley 0 1 0 0 0
## Siruis Black 0 1 0 0 0
## Severus Snape 0 0 0 0 1
## Alastor Moody 0 0 0 1 0
hp_aff <- graph_from_biadjacency_matrix(hp_mat)
shapes <- c("circle", "square")
colors <-c("green", "orange")
par(mar =c(5,0,2,0))
plot(hp_aff, vertex.color=colors[V(hp_aff)$type+1],
vertex.shape=shapes[V(hp_aff)$type+1], vertex.label.cex = 0.5, vertex.size = 7, main = "Harry Potter", sub = "Characters Connected to Groups")
Now we have our network object, we can use the bipartite_projection() argument to calculate the two one mode graphs that represent the duality within this graph. In this case, connections between characters based on shared affiliation to groups and connections between groups based on shared characters who affiliate with them.
hp_project <- bipartite_projection(hp_aff)
The hp_project object now has the symbolic ties (weighted) that exists between the characters (stored as proj1) and goups (stored as proj 2)
hp_project
## $proj1
## IGRAPH 747abca UNW- 122 3105 --
## + attr: name (v/c), weight (e/n)
## + edges from 747abca (vertex names):
## [1] Albus Dumbledore--Remus Lupin
## [2] Albus Dumbledore--Molly Weasley
## [3] Albus Dumbledore--Siruis Black
## [4] Albus Dumbledore--Severus Snape
## [5] Albus Dumbledore--Alastor Moody
## [6] Albus Dumbledore--Minerva McGonagall
## [7] Albus Dumbledore--Rubeus Hagrid
## [8] Albus Dumbledore--Kingsley Shacklebolt
## + ... omitted several edges
##
## $proj2
## IGRAPH 747abe5 UNW- 9 22 --
## + attr: name (v/c), weight (e/n)
## + edges from 747abe5 (vertex names):
## [1] Phoenix --Prefect Phoenix --Gryffindor
## [3] Phoenix --Death.Eaters Phoenix --Slytherin
## [5] Phoenix --Hufflepuff Phoenix --Ravenclaw
## [7] Phoenix --Dumbeldore.s.Army Dumbeldore.s.Army--Prefect
## [9] Dumbeldore.s.Army--Gryffindor Dumbeldore.s.Army--Ravenclaw
## [11] Dumbeldore.s.Army--Hufflepuff Death.Eaters --Slytherin
## [13] Death.Eaters --Gryffindor Death.Eaters --Prefect
## [15] Death.Eaters --Ravenclaw
## + ... omitted several edges
This pulls out the duality that exists between the characters based on their mutual affiliation to groups
characters <- hp_project$proj1
This pulls out the duality that exists between the groups who share characters.
groups <-hp_project$proj2
Now we can analyse these dual/symbolic connections.
groups
## IGRAPH 747abe5 UNW- 9 22 --
## + attr: name (v/c), weight (e/n)
## + edges from 747abe5 (vertex names):
## [1] Phoenix --Prefect Phoenix --Gryffindor
## [3] Phoenix --Death.Eaters Phoenix --Slytherin
## [5] Phoenix --Hufflepuff Phoenix --Ravenclaw
## [7] Phoenix --Dumbeldore.s.Army Dumbeldore.s.Army--Prefect
## [9] Dumbeldore.s.Army--Gryffindor Dumbeldore.s.Army--Ravenclaw
## [11] Dumbeldore.s.Army--Hufflepuff Death.Eaters --Slytherin
## [13] Death.Eaters --Gryffindor Death.Eaters --Prefect
## [15] Death.Eaters --Ravenclaw
## + ... omitted several edges
par(mar =c(0,0,2,0))
set.seed(123)
plot(groups, vertex.shape = "square", edge.width = E(groups)$weight, main = "Projection 2: Groups that Share Characters")
plot(groups, vertex.shape = "square", vertex.size = degree(groups))
plot(groups, vertex.shape = "square", vertex.size = betweenness(groups))
We can even try identifying communities of groups that share characters. This would indicate if there are certain groups that share more groups together than others.
walk_grou <- cluster_walktrap(groups)
par(mar =c(5,0,2,0))
set.seed(123)
plot(walk_grou, groups, vertex.shape = "square", edge.width = E(groups)$weight, main = "Projection 2: Groups that Share Characters", sub = "Walktrap Communities Highlighted")
Comparing to louvain algorithm
louv_group <- cluster_louvain(groups)
set.seed(123)
plot(louv_group, vertex.shape = "square", groups)
Those of you who know Harry Potter, you clearly see a “good” and “bad” divide in this network visual.
Let’s do the same for characters connected to other characters by mutual affiliation to groups.
set.seed(123)
plot(characters, vertex.color = "green", vertex.label.cex = 0.5, vertex.size = 7, layout = layout_with_fr(characters), main = "Projection 1: Characters who Share Groups")
What about some centrality scores in this network?
set.seed(123)
plot(characters, vertex.color = "green", vertex.size = degree(characters)/10, vertex.label.cex = 0.5)
set.seed(123)
plot(characters, vertex.color = "green", vertex.size = betweenness(characters)/20, vertex.label.cex = 0.5)
Communities of characters
louv_charac <- cluster_louvain(characters)
set.seed(123)
plot(louv_charac, characters, vertex.label.cex = 0.4, layout = layout_with_fr(characters), vertex.size = 7)
Walktrap does well with weighted, dense networks.
walk_characc <- cluster_walktrap(characters)
set.seed(123)
plot(walk_characc, characters, vertex.label.cex = 0.5, vertex.size = 7, layout = layout_with_fr(characters), main = "Projection 1: Characters who Share Groups", sub = "Walktrap Communities Highlighted")
In sum, you have now taken a two mode network and created two one mode networks based on the duality of the characters and groups in Harry Potter. You have also identified certain groups and characters that are very prominent or play influential roles within these networks. You have also identified communities of characters and groups within these projected networks. When analysing these you want to talk about them as projected or implied networks. Refer to Breiger’s (1974) paper on duality of persons and groups for more on discussing such networks.
Breiger, R. L. (1974). The duality of persons and groups. Social Forces, 53(2), 181-190.