Multiplot with ggplot

[This article was first published on R Code – Geekcologist , and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

ggplot is a wonderful package to make beautiful plots in R. For those who still just use standard R plots I really suggest you give a look at ggplot. The ggplot syntax may look a bit strange in the beginning but there are really good tutorials out there to help you start. In this post I’ll explain how to plot multiple ggplots on one page, similar to what par(mfrow=c(nlines, ncolumns)) can do with standard plots, and how to produce and store ggplots inside a for loop.

First I’ll begin by using ggplot inside a for loop. Contrary to standard plots which can be stored directly on a list, ggplot is bit trickier. If you try it with ggplot you will end up with a list with multiple plots of the same last plot of the loop. This happens because when ggplot “saves” a plot, in reality it stores a data frame and the plotting parameters, and when these plot expressions are evaluated at the end of the loop, the last i is the one used for all the evaluations and therefor you end up with multiple copies of the last plot.

Since for loops have no separate variable scope (i.e. they are performed in the current environment), we need to make it a local variable (simply re-assigning it will do the trick), with the use of local wrapping the for block, with the form for (i in 1:4) local({i = i; … rest of the loop … }) and use the operator <<- to assign the plots to the list inside the loop. This way, when plots are stored in the list they are evaluated with the local variable i.

A simple example using the iris data is presented below:

library(ggplot2)
data("iris")

out_list <- list()
for(i in 1:4)
local({
i <- i
aux_plot <- ggplot(data=iris, aes(x=iris[[i]]))+
geom_histogram()+
xlab(colnames(iris)[i])
out_list[[i]] <<- aux_plot
})

To plot multiple graphs in one page I adapted a function from cookbook-r. To the original function I’ve added the possibility to include titles per column or a single title for all the columns in the page. The function is presented below. Please note that the package “grid” is needed to run this function.

# Adapted from:
# http://www.cookbook-r.com/Graphs/Multiple_graphs_on_one_page_(ggplot2)/

# objects can be passed in ..., or to plotlist (as a list of ggplot objects)
# - cols:   Number of columns in layout
# - layout: A matrix specifying the layout. If present, 'cols' is ignored.
#
# If the layout is something like matrix(c(1,2,3,3), nrow=2, byrow=TRUE),
# then plot 1 will go in the upper left, 2 will go in the upper right, and
# 3 will go all the way across the bottom.
# 
# Title can be passed as c("title1", "title2"). If only one title is
# provided it will be used as single title for all columns. If several titles
# are provided each column will then have it's own title.
#
# Title size, font and face can also be provided

multiplot <- function(..., plotlist = NULL, cols = 1, layout = NULL, title = NULL, 
                      fontsize = 14, fontfamily = "Helvetica", fontface = "bold") {
  require(grid)
  plots <- c(list(...), plotlist)
  numPlots = length(plots)
  if (is.null(layout)) {
    layout <- matrix(seq(1, cols * ceiling(numPlots/cols)),
                     ncol = cols, nrow = ceiling(numPlots/cols))
  }
   if (length(title)>0){
    layout <- rbind(rep(0, ncol(layout)), layout)
  }
   if (numPlots==1) {
    print(plots[[1]])
   } else {
    grid.newpage()
    pushViewport(viewport(layout = grid.layout(nrow(layout), 
                          ncol(layout), 
                          heights = if (length(title)>0) {unit(c(0.5, rep(5,nrow(layout)-1)), "null")}
                          else {unit(c(rep(5, nrow(layout))), "null")})))
    if(length(title) > 1){
    ncols <- 1:ncol(layout)
    for(i in seq(ncols)){
      grid.text(title[i], 
                              vp = viewport(layout.pos.row = 1, layout.pos.col = i),
                              gp = gpar(fontsize = fontsize, fontfamily = fontfamily, fontface = fontface))
    }
    } else {
      grid.text(title, 
                vp = viewport(layout.pos.row = 1, layout.pos.col = 1:ncol(layout)),
                gp = gpar(fontsize = fontsize, fontfamily = fontfamily, fontface = fontface))
    }
    for (i in 1:numPlots) {
      matchidx <- as.data.frame(which(layout == i, arr.ind = TRUE))
       print(plots[[i]], vp = viewport(layout.pos.row = matchidx$row,
                                      layout.pos.col = matchidx$col))
    }
  }
}

As an example, the multiplot function could be used to plot the previous generated ggplot list:

library(grid)
multiplot(plotlist = out_list, cols = 2, title = c("title 1", "title 2"))

To leave a comment for the author, please follow the link and comment on their blog: R Code – Geekcologist .

R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Never miss an update!
Subscribe to R-bloggers to receive
e-mails with the latest R posts.
(You will not see this message again.)

Click here to close (This popup will not appear again)