08. Plotting Networks: Directed Networks

**Feb 9, 2013: These codes seem to be working ok in igraph v0.6, R2.15. 

Another major class of networks is a "directed network", aka "directed graph", aka "digraph". Simply, this means that edges have directionality (i.e., are "asymmetric"). In an example that I would use, an individual exhibits aggression to another individual, thus this interaction is directional (i.e., aggression FROM node A TO node B). In such cases, edges would be drawn as an arrow. In this section, I will simply describe how to draw such directed networks in R.

Let's start with what the adjacency matrix will look like. In an undirected network, the adjacency matrix is symmetrical. That is, the values of the matrix are mirror-images across the diagonal. In contrast, the adjacency matrix for an directed network is asymmetrical. Let's demonstrate this with a simple example. Let's say we have a three-node network, and an edge goes from node A to node B, and a second edge goes from node B to node C.
(By the way, this is the code for making the above figure):
layout=matrix(c(2,1, 1,0, 3,0),byrow=TRUE,nrow=3)
plot.igraph(g,layout=layout,edge.color="black",vertex.color="white", vertex.label=c("A","B","C"),vertex.size=20,vertex.label.cex=3, vertex.label.color="black",edge.width=3,edge.arrow.size=1.2,edge.arrow.width=1.2)

Theadjacency matrix of the above network would look like this:

  A B C
 A 0 1 0
 B 0 0 1
 C 0 0 0

By convention, the origin of the directed edge (i.e., the node that the arrow points from) are the rows, and the recipients of the edges are the columns.

More simply, this network can be represented as an edge list, in which the first column represents the origins of the edges and the nodes on the second column receive the edges:
 Node 1 
 Node 2
 A B
 B C

Ok, now let's up the ante a bit. We will now create an 8-node network with edge weights. This is modified from the section on plotting weighted networks. The following mock data is available as sample_weighted_directed_adjmatrix.csv at the bottom of the page:

 B C D E F G
 A 0 8 0 1 0 0 0
 B 0 0 0 15
 0 1 0
 0 1 0 0 0 0 0
 D 1 15 0 0 1 5 0
 E 0 0 0 1 0 0 0
 F 10 0 0 0 7 0 1
 G 0 0 0 0 0 1 0

I want to point out that there are four possible types of dyadic relationships in a weighted, directed network:
(1) no interactions -- cells on both sides of the diagonal are zero. Example: A and C
(2) an unidirectional interaction -- non-zero value on one side of the diagonal, and zero on the other. Example: A and B
(3) symmetrically mutual interaction -- cells on both sides have an equal, non-zero value. Example: E and D
(4) asymmetical mutual interaction -- cells on both sides have non-zero values, but the values are different.

I have conveniently omitted any examples of type 4, the asymmetric mutual interaction. This is because it requires a slightly different graphical representation, where each mutual dyads are connected by two directed edges. I will deal with this later in this section. First, let's just deal with the simple case.

A simple way to draw weighted, directed network:

To plot this weighted, directed network using igraph:

#import the sample_dw_adj.csv file:
dat=read.csv(file.choose(),header=TRUE,row.names=1,check.names=FALSE) # read .csv file
net=graph.adjacency(m,mode="directed",weighted=TRUE,diag=FALSE) #the only difference between this and the weighted network code is that mode="directed"

plot.igraph(net,vertex.label=V(net)$name,layout=layout.fruchterman.reingold, vertex.label.color="black",edge.color="black",edge.width=E(net)$weight/3, edge.arrow.size=1.5)
... and you'll get something like this:

You can draw a similar network in statnet:


dat=read.csv(file.choose(),header=TRUE,row.names=1,check.names=FALSE) # read the sample_dw_adj.csv file
net=network(m,matrix.type="adjacency",directed=FALSE,ignore.eval=FALSE, names.eval="value")
# recall that this creates a network object from the matrix, storing the edge values as an edge attribute called "value"

gplot(net,gmode="digraph",edge.lwd=net%e%'value',label=net%v%'vertex.names', arrowhead.cex=2)

The resulting figure will look something like this:

Note that in both igraph and statnet, the arrowhead sizes are uniform for all edges, so if one of your edges is really thick, it might be difficult to see the arrowhead. You can make all the arrowheads larger, but then your thin edges look weird. I'm not sure what the solution is, except to tinker with the proportional edge widths based on the edge weight you have, and come up with something where the thickest and thinnest edges look fairly normal.

Drawing networks with mutual edges with asymmetical weights:

This turns out to be very difficult to do in statnet, as far as I can tell. This is because the when you create the 'edge values' using names.eval="value" (4th code line in above code set), it adds the asymmetrical edges pointing both ways and splits it evenly between the two sides of the adjacency matrix. That is, the default in statnet is to create a weighted, undirected network. I have not played around with the codes enough to see how you can keep the asymmetrical information (perhaps using edgelist?). This isn't to say that statnet can't calculated network metrics with this type of information, but it seems difficult to draw them.

In contrast, the task of dealing with asymmetrical weighted, directed networks seems to be easier in igraph. Let's play with this adjacency matrix, available as sample_weighted_directed_adjmatrix_2 below.
 A B C D E
 8 0 5 0
 B 3 0 0 2 0
 C 0 1 0 0 0
 D 0 15 0 0 12
 E 0 0 0 3 0

The code is basically the same as above, except there is now an edge.curved=TRUE:

plot.igraph(net,vertex.label=V(net)$name,layout=layout.fruchterman.reingold, vertex.label.color="black",edge.color="black",edge.width=E(net)$weight/3, edge.arrow.size=1.5,edge.curved=TRUE)

And this will produce a figure that looks like this.

You can see that when you produce curved directed network plots in igraph, it shows you the asymmetry in edge weights.

Dai Shizuka,
Mar 15, 2015, 7:37 AM