43

I have a list, p, where each element of p is a list of ggplot2 plotting objects.

I would like to output a single pdf containing all the plots in p such that the plots in p[[1]] are on page 1, the plots in p[[2]] are on page 2, etc. How might I do this?

Here's some example code to provide you with the data structure I'm working with--apologies for the boring plots, I picked variables at random.

require(ggplot2)
p <- list()

cuts <- unique(diamonds$cut)
for(i in 1:length(cuts)){
    p[[i]] <- list()
    dat <- subset(diamonds, cut==cuts[i])
    p[[i]][[1]] <- ggplot(dat, aes(price,table)) + geom_point() + 
        opts(title=cuts[i])
    p[[i]][[2]] <- ggplot(dat, aes(price,depth)) + geom_point() + 
        opts(title=cuts[i])
}
Michael
  • 5,808
  • 4
  • 30
  • 39
  • 1
    Here's a potential start: `require(gridExtra); do.call("grid.arrange",p[[i]])`. That will plot the ggplot objects in p[[i]] in a single device, arranging them nicely. – Michael Sep 02 '12 at 08:02
  • 1
    Also look into the gridExtra package. I think that should get you most of the way there – chandler Sep 02 '12 at 08:04

8 Answers8

30

This solution is independent of whether the lengths of the lists in the list p are different.

library(gridExtra)

pdf("plots.pdf", onefile = TRUE)
for (i in seq(length(p))) {
  do.call("grid.arrange", p[[i]])  
}
dev.off()

Because of onefile = TRUE the function pdf saves all graphics appearing sequentially in the same file (one page for one graphic).

Sven Hohenstein
  • 80,497
  • 17
  • 145
  • 168
  • 6
    For me, this results in a damaged PDF that can't be opened. The plots look fine individually. Any tips? – half-pass Jul 14 '15 at 20:54
  • opts() did not work for me on `ggplot2_2.2.1` and `R version 3.3.2 `. Use `+ ggtitle(cuts[i])` instead. – bud.dugong Feb 28 '17 at 12:20
  • 4
    @half-pass, I had the same issue. In my case, doing an explicit `print()` on the ggplot2 object solved the problem (i.e. save plot to object, then print it, for each iteration of the loop). – CoderGuy123 Jun 25 '18 at 16:07
  • 1
    `seq_along(p)` could be used instead of `seq(length(p))` – Andre Elrico Feb 27 '19 at 09:24
  • For others who run into trouble employing this: note that this solution really only works if the ggplot objects are in a list. If you add them as separate arguments to grid.arrange, you will get several pages in the pdf with only the last one being the combination you want. – Jakob May 14 '20 at 11:11
24

Here is the most elegant solution to exporting a list of ggplot objects into a single pdf file using ggplot2::ggsave() and gridExtra::marrangeGrob().

library(ggplot2)
library(gridExtra)

Let's say you create multiple plots using lapply()

p <- lapply(names(mtcars), function(x) {
  ggplot(mtcars, aes_string(x)) + 
    geom_histogram()
})

Save list of p plots:

ggsave(
   filename = "plots.pdf", 
   plot = marrangeGrob(p, nrow=1, ncol=1), 
   width = 15, height = 9
)
Masood Sadat
  • 1,247
  • 11
  • 18
18

Here is a simpler version of Sven's solution for the R beginners who would otherwise blindly use the do.call and nested lists that they neither need nor understand. I have empirical evidence. :)

library(ggplot2)
library(gridExtra)

pdf("plots.pdf", onefile = TRUE)
cuts <- unique(diamonds$cut)
for(i in 1:length(cuts)){
    dat <- subset(diamonds, cut==cuts[i])
    top.plot <- ggplot(dat, aes(price,table)) + geom_point() + 
        opts(title=cuts[i])
    bottom.plot <- ggplot(dat, aes(price,depth)) + geom_point() + 
        opts(title=cuts[i])
    grid.arrange(top.plot, bottom.plot)
}
dev.off()
chris
  • 2,473
  • 1
  • 29
  • 28
7

I've tried some of these solutions but with no success. I researched a little more and found a solution that worked perfectly for me. It saves all my graphics in a single pdf file, each chart on one page.

library(ggplot2)


pdf("allplots.pdf",onefile = TRUE)
for(i in glist){
   tplot <- ggplot(df, aes(x = as.factor(class), y = value))
   print(tplot)
}
dev.off()
Pedro Henrique
  • 168
  • 1
  • 2
  • 8
5

Here's one solution, but I don't particularly like it:

ggsave("test.pdf", do.call("marrangeGrob", c(unlist(p,recursive=FALSE),nrow=2,ncol=1)))

The problem is that it relies on there being the same number of plots in each group. If all(sapply(p, length) == 2) were false, then it would break.

Michael
  • 5,808
  • 4
  • 30
  • 39
4

A solution that worked for me with ggpubr package (package on Github, code for installation: devtools::install_github("kassambara/ggpubr")).

Let's say you have 4 plots p1, p2, p3 and p4.

library(ggpubr)
multi.page <- ggarrange(p1,p2,p3,p4, nrow=1, ncol=1) # for one plot per page
multi.page[[1]] # for seeing the first plot
ggexport(multi.page, filename="my-plots.pdf")

More examples of ggpubr use: http://www.sthda.com/english/articles/24-ggpubr-publication-ready-plots/81-ggplot2-easy-way-to-mix-multiple-graphs-on-the-same-page/

cha.brault
  • 41
  • 2
3

Here's a function based on Sven's approach, including the roxygen2 documentation and an example.

#' Save list of ggplot2 objects to single pdf
#'
#' @param list (list) List of ggplot2 objects.
#' @param filename (chr) What to call the pdf.
#'
#' @return Invisible NULL.
#' @export
#'
#' @examples
#' #plot histogram of each numeric variable in iris
#' list_iris = map(names(iris[-5]), ~ggplot(iris, aes_string(.)) + geom_histogram())
#' #save to a single pdf
#' GG_save_pdf(list_iris, "test.pdf")
GG_save_pdf = function(list, filename) {
  #start pdf
  pdf(filename)

  #loop
  for (p in list) {
    print(p)
  }

  #end pdf
  dev.off()

  invisible(NULL)
}
CoderGuy123
  • 6,219
  • 5
  • 59
  • 89
0

A nice solution without the gridExtra package:

library(plyr)
library(ggplot2)

li = structure(p, class = c("gglist", "ggplot"))
print.gglist = function(x, ...) l_ply(x, print, ...)
ggsave(li, file = "test.pdf")
fc9.30
  • 2,293
  • 20
  • 19