03. Affiliation Networks/Bipartite Networks

** This page is now updated for igraph version 0.6.3 in R 2.15.2 (Feb 2, 2013)

Here, we are going to deal with a slightly different kind of networks, called bipartite networks. Here 
are two examples of bipartite networks:

  

These two graphs are the same, but they represent two different scenarios.
The first scenario is if have two types of actors, and one type of actor can only interact with the other type of actor. For example, picture a network of pollination interactions between plants and their pollinators. Pollinators only pollinate plants, so there are no edges between pollinators. Similarly, there are no edges between plants.
The second scenario is when you have individuals belong to particular groups. Edges represent membership of individuals in groups. These are sometimes called affiliation networks. Here, you can imagine 'individuals' being one set of nodes, and 'groups' being another set of nodes. Individuals can only be connected to groups, and vice versa. In this case, you might actually be interested in generating a social network of individuals based on co-membership. We can do this using what is called a one-mode projection, or bipartite projection. We will touch on this a little later. 



We'll use the example of 5 individuals belonging to 4 different groups.

A=c(1,1,0,0)
B=c(1,0,1,0)
C=c(1,0,1,0)
D=c(0,1,0,1)
E=c(0,0,1,1)
bm=matrix(c(A,B,C,D,E),nrow=5,byrow=TRUE)
dimnames(bm)=list(c("A","B","C","D","E"),c("Group1","Group2","Group3","Group4"))
bm
This should create a matrix that looks like this:

> bm
  Group1 Group2 Group3 Group4
A      1      1      0      0
B      1      0      1      0
C      1      0      1      0
D      0      1      0      1
E      0      0      1      1

You can convert this directly into an igraph object:
bg=graph.incidence(bm)
> bg
IGRAPH UN-B 9 10 -- 
+ attr: type (v/x), name (v/c)

From the first line, we see that we have created an Undirected, Named graph that is Bipartite and has 9 nodes and 10 edges. The second line tells us that there are two vertex attributes: type and name. Let's check these out:

V(bg)$type
V(bg)$name

> V(bg)$type
[1] FALSE FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE
> V(bg)$name
[1] "A"      "B"      "C"      "D"      "E"      "Group1" "Group2"
[8] "Group3" "Group4"

You see that igraph has arranged both individuals and groups as nodes, and then created the attribute 'type' to indicate that these are two distinct classes of nodes. 

Now let's see what this network looks like:


shapes=c("circle","circle","circle","circle","circle","square","square","square","square") # make the first 5 nodes circles, and the rest squares.

labeldistances=c(0,0,0,0,0,0.6,0.6,0.6,0.6) #The labels for the first 5 nodes will be in the center of the node, but the others will be offset

plot(bg,vertex.shape=shapes,vertex.label.degree=-pi/2,vertex.label.dist=labeldistances,vertex.color=V(bg)$type) #The vertex.label.degree= argument tells where to put the label, in radians.

You can see that this network is the same as the second example I showed at the top of this page. 

---------Bipartite Projections----------

Let's say you are interested in the co-membership relations between the individuals. What you need to do is to create a one-mode projection of the bipartite network. Note that there are actually TWO ways to project a bipartite network: You can make a co-membership network of nodes, or a network of groups that share members. You can get both of these at once with this function:

pr=bipartite.projection(bg)
pr
which will give you:
> pr
$proj1
IGRAPH UNWB 5 7 -- 
+ attr: type (v/x), name (v/c), weight (e/n)

$proj2
IGRAPH UNWB 4 4 -- 
+ attr: type (v/x), name (v/c), weight (e/n)

you can see here that the bipartite projection has given us a list object with two graphs: pr$proj1 (5 vertices and 6 edges) and pr$proj2 (4 vertices and 6 edges). 
get.adjacency(pr$proj1,sparse=FALSE,attr="weight")
will give you the adjacency matrix of the first projection:

  A B C D E
A 0 1 1 1 0
B 1 0 2 0 1
C 1 2 0 0 1
D 1 0 0 0 1
E 0 1 1 1 0

You can plot this projection:
plot(pr$proj1,edge.width=E(pr$proj1)$weight^2,edge.color="black",vertex.label=V(pr$proj1)$name)

Now, you have an affiliation network of individuals based on co-membership in groups.

... at this point, it is useful to know that bipartite projections are not magic. In fact, you can project the bipartite network manually by multiplying the bipartite/affiliation matrix by its transpose (the same matrix flipped along its diagonal), then setting the diagonal to 0:

aff=bm%*%t(bm)
diag(aff)=0
> aff
  A B C D E
A 0 1 1 1 0
B 1 0 2 0 1
C 1 2 0 0 1
D 1 0 0 0 1
E 0 1 1 1 0

You can see that this matrix is identical to the first projection of the bipartite matrix shown above. 

Comments