Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Arrange n ggplots into lower triangle matrix shape

Tags:

r

ggplot2

I have n ggplot objects that will always have the correct number to make a lower triangle of a matrix (no diagonals). How can I arrange them in this order:

1
2 3
4 5 6
7 8 9 10

to form a grid (n = 10 here)?

Here is data to make n plots and how I'd like it to look of I had n = 6.

n <- sample(1:4, 1)
N <- sum(n:1)

library(ggplot2)
theplot <- ggplot(mtcars, aes(mpg, hp)) + geom_point()
plots <- lapply(1:N, function(i) theplot)
plots <- mapply(function(x, y) x + ggtitle(y), plots, 
    paste("PLOT", seq_along(plots)), SIMPLIFY=FALSE)

enter image description here

I suspect gridExtra may be useful here but there are blank panes. I'm open to base or add on package ideas.

like image 814
Tyler Rinker Avatar asked Apr 10 '14 18:04

Tyler Rinker


3 Answers

You can pass a matrix layout to grid.arrange,

library(ggplot2)
library(gridExtra)
plots <- lapply(1:10, function(id) ggplot() + ggtitle(id))

m <- matrix(NA, 4, 4)
m[lower.tri(m, diag = T)] <- 1:10
grid.arrange(grobs = plots, layout_matrix = m)

enter image description here

like image 153
baptiste Avatar answered Oct 13 '22 08:10

baptiste


Here's a fairly painless approach, which shouldn't be too difficult to generalize:

library(gridExtra) ## for grid.arrange()
ng <- nullGrob()
grid.arrange(plots[[1]], ng,         ng,
             plots[[2]], plots[[3]], ng,
             plots[[4]], plots[[5]], plots[[6]])

enter image description here

like image 41
Josh O'Brien Avatar answered Oct 13 '22 08:10

Josh O'Brien


I was a fan of wq::layOut for arranging ggplots when grid.arrange seems too complicated (though Josh shows that it works just fine here). If you use a new device, you don't have to worry about the holes.

layOut was removed from the wq package, so I include the code here, renamed to lay_out. It's at the bottom, after the usage examples.

lay_out(list(plots[[1]], 1, 1), # each arg is list(plot, row(s), column(s))
       list(plots[[2]], 2, 1),
       list(plots[[3]], 2, 2))

layOut1

It's main strength is when you have different sized plots.

lay_out(list(plots[[1]], 1, 1:3), 
        list(plots[[2]], 2, 1),
        list(plots[[3]], 2, 2),
        list(plots[[4]], 3, 1:2),
        list(plots[[5]], 2:3, 3))

layOut2

I think anything you could do with layOut can be done with nested grid.arrange and arrangeGrob calls, but it's often easier to think about this way.

#' Arranging ggplots
#' 
#' Provides a \code{layout}-like interface for arranging ggplots of different 
#' sizes.
#' 
#' @param ... Each argument should be of the form \code{list(plot, rows, 
#' columns)}, where \code{plot} is a ggplot (or similar), and \code{rows} and 
#' \code{columns} are consecutive sequences indicating the row and column 
#' numbers for \code{plot} to span.
#' 
#' @author Alan D. Jassby and James E. Cloern (originally from the \code{wq} 
#' package).
#' 
#' @examples
#' \dontrun{
#' gg <- ggplot(mtcars, aes(x = hp, y = mpg)) + geom_point()
#' layOut(list(gg, 1:2, 1:3),
#'        list(gg, 3, 1:2),
#'        list(gg, 3, 3))
#' }
#' 
#' @export
lay_out <- function(...) {

    x <- list(...)
    n <- max(sapply(x, function(x) max(x[[2]])))
    p <- max(sapply(x, function(x) max(x[[3]])))
    grid::pushViewport(grid::viewport(layout = grid::grid.layout(n, p)))    

    for (i in seq_len(length(x))) {
        print(x[[i]][[1]],
              vp = grid::viewport(layout.pos.row = x[[i]][[2]], 
                                  layout.pos.col = x[[i]][[3]]))
    }
}
like image 28
Gregor Thomas Avatar answered Oct 13 '22 07:10

Gregor Thomas