Skip to content

Network visualization in R with the igraph package

In this post I showed a visualization of the organizational network of my department. Since several people asked for details how the plot has been produced, I will provide the code and some extensions below. The plot has been done entirely in R (2.14.01) with the help of the igraph package. It is a great package but I found the documentation somewhat difficult to use, so hopefully this post can be a helpful introduction to network visualization with R. Here we go:

# Load the igraph package (install if needed)

require(igraph)

# Data format. The data is in 'edges' format meaning that each row records a relationship (edge) between two people (vertices).
# Additional attributes can be included. Here is an example:
#	Supervisor	Examiner	Grade	Spec(ialization)
#	AA		BD		6	X	
#	BD		CA		8	Y
#	AA		DE		7	Y
#	...		...		...	...
# In this anonymized example, we have data on co-supervision with additional information about grades and specialization. 
# It is also possible to have the data in a matrix form (see the igraph documentation for details)

# Load the data. The data needs to be loaded as a table first: 

bsk<-read.table("http://www.dimiter.eu/Data_files/edgesdata3.txt", sep='t', dec=',', header=T)#specify the path, separator(tab, comma, ...), decimal point symbol, etc.

# Transform the table into the required graph format:
bsk.network<-graph.data.frame(bsk, directed=F) #the 'directed' attribute specifies whether the edges are directed
# or equivelent irrespective of the position (1st vs 2nd column). For directed graphs use 'directed=T'

# Inspect the data:

V(bsk.network) #prints the list of vertices (people)
E(bsk.network) #prints the list of edges (relationships)
degree(bsk.network) #print the number of edges per vertex (relationships per people)

# First try. We can plot the graph right away but the results will usually be unsatisfactory:
plot(bsk.network)

Here is the result:

Not very informative indeed. Let’s go on:

 
#Subset the data. If we want to exclude people who are in the network only tangentially (participate in one or two relationships only)
# we can exclude the by subsetting the graph on the basis of the 'degree':

bad.vs<-V(bsk.network)[degree(bsk.network)<3] #identify those vertices part of less than three edges
bsk.network<-delete.vertices(bsk.network, bad.vs) #exclude them from the graph

# Plot the data.Some details about the graph can be specified in advance.
# For example we can separate some vertices (people) by color:

V(bsk.network)$color<-ifelse(V(bsk.network)$name=='CA', 'blue', 'red') #useful for highlighting certain people. Works by matching the name attribute of the vertex to the one specified in the 'ifelse' expression

# We can also color the connecting edges differently depending on the 'grade': 

E(bsk.network)$color<-ifelse(E(bsk.network)$grade==9, "red", "grey")

# or depending on the different specialization ('spec'):

E(bsk.network)$color<-ifelse(E(bsk.network)$spec=='X', "red", ifelse(E(bsk.network)$spec=='Y', "blue", "grey"))

# Note: the example uses nested ifelse expressions which is in general a bad idea but does the job in this case
# Additional attributes like size can be further specified in an analogous manner, either in advance or when the plot function is called:

V(bsk.network)$size<-degree(bsk.network)/10#here the size of the vertices is specified by the degree of the vertex, so that people supervising more have get proportionally bigger dots. Getting the right scale gets some playing around with the parameters of the scale function (from the 'base' package)

# Note that if the same attribute is specified beforehand and inside the function, the former will be overridden.
# And finally the plot itself:
par(mai=c(0,0,1,0)) 			#this specifies the size of the margins. the default settings leave too much free space on all sides (if no axes are printed)
plot(bsk.network,				#the graph to be plotted
layout=layout.fruchterman.reingold,	# the layout method. see the igraph documentation for details
main='Organizational network example',	#specifies the title
vertex.label.dist=0.5,			#puts the name labels slightly off the dots
vertex.frame.color='blue', 		#the color of the border of the dots 
vertex.label.color='black',		#the color of the name labels
vertex.label.font=2,			#the font of the name labels
vertex.label=V(bsk.network)$name,		#specifies the lables of the vertices. in this case the 'name' attribute is used
vertex.label.cex=1			#specifies the size of the font of the labels. can also be made to vary
)

# Save and export the plot. The plot can be copied as a metafile to the clipboard, or it can be saved as a pdf or png (and other formats).
# For example, we can save it as a png:
png(filename="org_network.png", height=800, width=600) #call the png writer
#run the plot
dev.off() #dont forget to close the device
#And that's the end for now.

Here is the result:

Still not perfect, but much more informative and aesthetically pleasing.

Additional information can be found on this guide to igraph which is in development, the examples here, and the official CRAN documentation of the package. Especially useful is this list of the plot attributes that can be tweaked. The plots can also be adjusted interactively using the tkplot function instead of plot, but the options for saving the resulting figure are limited.

Have fun with your networks!

Published inData visualizationNetwork analysisR

22 Comments

  1. Anonymous

    have you tried igraph::rglplot with the layout.fruchterman.reingold layout? Less practicable but nice to look at.

  2. Anonymous

    He already did try layout.fruchterman.reingold; it is there in the code. But thanks to the poster this was a very illustrative example.

  3. Anonymous

    Is there any way to cluster the same “specialisation” together?

    • Anonymous

      For example, BD, AA, CA, DE are having same specialization. How could I plot graph having all four clustering/sticking close to each other as “a group”. Pardon me, I am new on this. Hope the question is clear. Thanks in advance.

      • The distance between the nodes is a function of the relatedness as discovered in the data. So it would defeat the purpose of the network graph to specify the clusters in advance. What you can do is color members of the same specialization in the same color to see whether the specializations actually correspond to the clusters as discovered in the actual data. hope that helps.

  4. Sri

    I have just two columns in my matrix. A and B. I need to color my nodes with just 2 colors – that indicates nodes that belong to A and those that belong to B. eg:

    # k is a df with 2 cols – A and B
    k_mx <- as.matrix(k)
    k_mx_g <- graph.edgelist(k_mx, directed = FALSE)
    V(k_mx_g)$color = ?? ( want blue for A and red for B)

    • asc

      You can try:
      V(k_mx_g)$color <- ifelse(V(k_mx_g)$colorColumn == 1, "blue", "red")

      *colorColumn should be, for example 1 for blue, and 0 for red

  5. Is it possible when using the layout fruchterman.reingold to prevent the resulting graph from rotating? I’m trying to compary community identification but each time I call the graph(network…) the results are oriented differently. Different layouts (e.g. sphere) don’t rotate and I suspect it’s due to how the structure is identified, so it may be impossible.

    • Anonymous

      Why would you recalculate the layout? Calculate it the first time and then try comparing the community identification over the same layout.

  6. UN

    thank you for a quick overview – i am trying to graph an incidence matrix of 1200X2000 – any tips on graphing those?

  7. joe

    Hello.
    Hi.What is the difference between igraph and Rgraphviz? Or which one works better?

  8. fjarroyave

    when you have a really dense network, who do you avoid ovelapping ? thnx!

    • Anonymous

      Try making the individual dots almost transparent by manipulating the alpha setting. This would not work for really big networks, but for ones of moderate size is worth trtying

  9. Firman

    Hello there,
    I am trying to create a domain graph based on the path to conversion reports that I pulled from my system. For instance, the common path that users take to make a conversion (i.e. to sign up for credit card).

    Below are such data that I will see in the report:

    Path | Conversion
    abc.com>def.com>ghi.com | 20
    abc.com>xyz.com | 13
    def.com>jkl.com>mno.com | 10

    My end game will be a domain graph of these conversions which shows the most common domain that user will go to during the conversion process.

    Thank you.

    Cheers
    Firman

  10. Anonymous

    How can I programm multilayer networks in R?

Leave a Reply to Anonymous Cancel reply

Your email address will not be published.