¿Cómo puedo organizar un número arbitrario de ggplots usando grid.arrange?

Esto se publica de forma cruzada en el grupo de google ggplot2

Mi situación es que estoy trabajando en una función que genera un número arbitrario de plots (dependiendo de los datos de entrada proporcionados por el usuario). La función devuelve una lista de n plots, y me gustaría poner esas plots en formación 2 x 2. Estoy luchando con los problemas simultáneos de:

  1. ¿Cómo puedo permitir que se le otorgue la flexibilidad a un número arbitrario (n) de plots?
  2. ¿Cómo puedo también especificar que quiero que se distribuyan 2 x 2

Mi estrategia actual usa grid.arrange del paquete gridExtra . Probablemente no sea óptimo, especialmente desde que, y esta es la clave, no funciona . Aquí está mi código de muestra comentado, experimentando con tres ttwigs:

 library(ggplot2) library(gridExtra) x <- qplot(mpg, disp, data = mtcars) y <- qplot(hp, wt, data = mtcars) z <- qplot(qsec, wt, data = mtcars) # A normal, plain-jane call to grid.arrange is fine for displaying all my plots grid.arrange(x, y, z) # But, for my purposes, I need a 2 x 2 layout. So the command below works acceptably. grid.arrange(x, y, z, nrow = 2, ncol = 2) # The problem is that the function I'm developing outputs a LIST of an arbitrary # number plots, and I'd like to be able to plot every plot in the list on a 2 x 2 # laid-out page. I can at least plot a list of plots by constructing a do.call() # expression, below. (Note: it totally even surprises me that this do.call expression # DOES work. I'm astounded.) plot.list <- list(x, y, z) do.call(grid.arrange, plot.list) # But now I need 2 x 2 pages. No problem, right? Since do.call() is taking a list of # arguments, I'll just add my grid.layout arguments to the list. Since grid.arrange is # supposed to pass layout arguments along to grid.layout anyway, this should work. args.list <- c(plot.list, "nrow = 2", "ncol = 2") # Except that the line below is going to fail, producing an "input must be grobs!" # error do.call(grid.arrange, args.list) 

Como solía hacer, me apretujé humildemente en la esquina, esperando ansiosamente la respuesta sagaz de una comunidad mucho más sabia que yo. Especialmente si estoy haciendo esto más difícil de lo necesario.

¡Ya casi estás ahí! El problema es que do.call espera que tus do.call estén en un objeto de list nombre. Los ha puesto en la lista, pero como cadenas de caracteres, elementos de lista sin nombre.

Creo que esto debería funcionar:

 args.list <- c(plot.list, 2,2) names(args.list) <- c("x", "y", "z", "nrow", "ncol") 

como Ben y Joshua señalaron en los comentarios, podría haber asignado nombres cuando creé la lista:

 args.list <- c(plot.list,list(nrow=2,ncol=2)) 

o

 args.list <- list(x=x, y=y, z=x, nrow=2, ncol=2) 

Prueba esto,

 require(ggplot2) require(gridExtra) plots <- lapply(1:11, function(.x) qplot(1:10,rnorm(10), main=paste("plot",.x))) params <- list(nrow=2, ncol=2) n <- with(params, nrow*ncol) ## add one page if division is not complete pages <- length(plots) %/% n + as.logical(length(plots) %% n) groups <- split(seq_along(plots), gl(pages, n, length(plots))) pl <- lapply(names(groups), function(g) { do.call(arrangeGrob, c(plots[groups[[g]]], params, list(main=paste("page", g, "of", pages)))) }) class(pl) <- c("arrangelist", "ggplot", class(pl)) print.arrangelist = function(x, ...) lapply(x, function(.x) { if(dev.interactive()) dev.new() else grid.newpage() grid.draw(.x) }, ...) ## interactive use; open new devices pl ## non-interactive use, multipage pdf ggsave("multipage.pdf", pl) 

Estoy respondiendo un poco tarde, pero me encontré con una solución en el R Graphics Cookbook que hace algo muy similar usando una función personalizada llamada multiplot . Quizás ayudará a otros que encuentren esta pregunta. También estoy agregando la respuesta ya que la solución puede ser más nueva que las otras respuestas a esta pregunta.

Múltiples gráficos en una página (ggplot2)

Esta es la función actual, aunque utilice el enlace anterior, ya que el autor señaló que se ha actualizado para ggplot2 0.9.3, lo que indica que puede cambiar nuevamente.

 # Multiple plot function # # ggplot 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. # multiplot <- function(..., plotlist=NULL, file, cols=1, layout=NULL) { require(grid) # Make a list from the ... arguments and plotlist plots <- c(list(...), plotlist) numPlots = length(plots) # If layout is NULL, then use 'cols' to determine layout if (is.null(layout)) { # Make the panel # ncol: Number of columns of plots # nrow: Number of rows needed, calculated from # of cols layout <- matrix(seq(1, cols * ceiling(numPlots/cols)), ncol = cols, nrow = ceiling(numPlots/cols)) } if (numPlots==1) { print(plots[[1]]) } else { # Set up the page grid.newpage() pushViewport(viewport(layout = grid.layout(nrow(layout), ncol(layout)))) # Make each plot, in the correct location for (i in 1:numPlots) { # Get the i,j matrix positions of the regions that contain this subplot 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)) } } } 

Uno crea objetos de trazado:

 p1 <- ggplot(...) p2 <- ggplot(...) # etc. 

Y luego los pasa a multiplot :

 multiplot(p1, p2, ..., cols = n)