27

In base graphics I can create a 4 panel pane of graphics by doing the following:

par(mfrow=c(2,2))
for (i in 1:4){
  plot(density(rnorm(100)))
}

which results in

enter image description here

I'd like to do the same sort of thing with ggplot2, but I can't figure out how to do it. I can't use facets because my real data, unlike this trivial example, is in very different structures and I want two graphs to be point charts and two to be histograms. How can do create panels or panes in ggplot2?

Gavin Simpson
  • 170,508
  • 25
  • 396
  • 453
JD Long
  • 59,675
  • 58
  • 202
  • 294
  • 2
    There is a good solution to this here: http://stackoverflow.com/questions/1532535/showing-multiple-axis-labels-using-ggplot2-with-facet-wrap-in-r/1532659#1532659 – Andrie Nov 03 '11 at 11:04
  • 1
    Similar question: http://stackoverflow.com/q/3305613/602276 – Andrie Nov 03 '11 at 11:06
  • 10
    It seems that [suggestion #6 on this answer](http://stackoverflow.com/questions/7843741/abbreviations-and-functions-in-preparation-for-a-programming-contest/7844369#7844369) applies to JD Long himself. That makes my day. :) – Iterator Nov 03 '11 at 13:06
  • @Iterator: this is hilarious. Thanks for sharing! – Matt Bannert Nov 03 '11 at 14:20
  • HA! Excellent. In Stack Overflow, much like in the economy as a whole, self serving behavior can have positive externalities! – JD Long Nov 07 '11 at 16:15

5 Answers5

34

Following Josh O'Brien's example: I'm surprised no one has mentioned grid.arrange from the gridExtra package yet:

library(gridExtra)
grid.arrange(q1,q2,q3,q4,q5,q6,nrow=3)

This seems to be mentioned here: multiple graphs in one canvas using ggplot2

For me, it's much easier than remembering all the viewport stuff.

Community
  • 1
  • 1
Ben Bolker
  • 211,554
  • 25
  • 370
  • 453
  • Great pointer. Nothing beats an already-packaged function, and I like that it's also set up to take options to `grid.layout()`. – Josh O'Brien Nov 03 '11 at 16:00
  • 4
    `grid.arrange` comes with a couple of enhancements to `multiplot`, e.g. i) handling of grid/lattice objects; ii) compatible with `ggsave`; iii) layout parameters; iv) default nrow/ncol; v) `arrangeGrob` version to avoid immediate drawing; vi) placeholders for titles on either sides; vii) as.table argument to reverse the drawing order. – baptiste Nov 03 '11 at 19:12
  • @baptiste, using grid.arrange to plot 2 graphics and saving them with ggsave resulted in only one graphic being saved on a pdf file. Are you sure that grid is compatible with ggsave? – Eduardo Jun 04 '13 at 11:23
  • 1
    @Eduardo you're sort of hijacking an old answer here, but in short: use `ggsave(file = "whatever.pdf", arrangeGrob(p1, p2, p3))` and a recent version of `gridExtra`. grid.arrange only displays, arrangeGrob is what you need to store the object. ggsave internally keeps track of the latest ggplot2 printed, but grid.arrange doesn't. – baptiste Jun 04 '13 at 11:33
19

EDIT: { Ben Bolker points to an even better option -- grid.arrange from the gridExtra package. If you're a ggplot2 user, though, the R Cookbook site is still worth a click-through. }

There's code for a nice multiplot function on this page of the R Cookbook (definitely worth a visit) that's useful for this sort of thing. Quoting directly from that site:

multiplot <- function(..., plotlist=NULL, cols) {
    require(grid)

    # Make a list from the ... arguments and plotlist
    plots <- c(list(...), plotlist)

    numPlots = length(plots)

    # Make the panel
    plotCols = cols                       # Number of columns of plots
    plotRows = ceiling(numPlots/plotCols) # Number of rows needed, calculated from # of cols

    # Set up the page
    grid.newpage()
    pushViewport(viewport(layout = grid.layout(plotRows, plotCols)))
    vplayout <- function(x, y)
        viewport(layout.pos.row = x, layout.pos.col = y)

    # Make each plot, in the correct location
    for (i in 1:numPlots) {
        curRow = ceiling(i/plotCols)
        curCol = (i-1) %% plotCols + 1
        print(plots[[i]], vp = vplayout(curRow, curCol ))
    }

}

Trying it out with 6 plots in a 3-by-2 layout (four of JD Long's plots, and two bonus ones!):

set.seed(2)
q1 <- ggplot(data.frame(x=rnorm(50)), aes(x)) + geom_density()
q2 <- ggplot(data.frame(x=rnorm(50)), aes(x)) + geom_density()
q3 <- ggplot(data.frame(x=rnorm(50)), aes(x)) + geom_density()
q4 <- ggplot(data.frame(x=rnorm(50)), aes(x)) + geom_density()
q5 <- ggplot(data.frame(x=rnorm(50)), aes(x)) + geom_density()
q6 <- ggplot(data.frame(x=rnorm(50)), aes(x)) + geom_density()

multiplot(q1, q2, q3, q4, q5, q6, cols=2)

gives this figure:

enter image description here

If the function doesn't quite fit your needs, at least it gives you a nice starting point!

Josh O'Brien
  • 159,210
  • 26
  • 366
  • 455
12

As of July 2018, there are several packages that help create multi-panel plots nicely. See examples below

library(ggplot2)
theme_set(theme_bw())

q1 <- ggplot(mtcars) + geom_point(aes(mpg, disp))
q2 <- ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear))
q3 <- ggplot(mtcars) + geom_smooth(aes(disp, qsec))
q4 <- ggplot(mtcars) + geom_bar(aes(carb))

egg package

library(grid)
library(egg)

ggarrange(q1, q2, q3, q4, 
          ncol = 2,
          top = "Plot title",
          bottom = textGrob(
            "This footnote is right-justified",
            gp = gpar(fontface = 3, fontsize = 10),
            hjust = 1, x = 1)
          )
#> `geom_smooth()` using method = 'loess' and formula 'y ~ x'

cowplot package

library(cowplot)

plot_grid(q1, q2, q3, q4, 
          ncol = 2,
          labels = "AUTO")
#> `geom_smooth()` using method = 'loess' and formula 'y ~ x'

patchwork package

library(patchwork)

q1 + q2 + q3 + q4 + 
  plot_layout(ncol = 2) +
  plot_annotation(title = "Plot title",
                  subtitle = "Plot subtitle",
                  tag_levels = 'A',
                  tag_suffix = ')')
#> `geom_smooth()` using method = 'loess' and formula 'y ~ x'

# complex layout 1
q1 + {
  q2 + plot_spacer() + {
    q3 + 
      q4 + 
      plot_layout(ncol = 1)
  }
} +
  plot_layout(ncol = 1)
#> `geom_smooth()` using method = 'loess' and formula 'y ~ x'

# complex layout 2
(q1 | q2 | q3) /
  q4
#> `geom_smooth()` using method = 'loess' and formula 'y ~ x'

# bonus: working with grob objects
p1 <- ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear))
table1 <- tableGrob(mtcars[1:6, 1:4])
patchwork::wrap_plots(list(p1, table1), nrow = 1) 

multipanelfigure package

library(magrittr)
library(multipanelfigure)

figure1 <- multi_panel_figure(columns = 2, rows = 2, panel_label_type = "none")
# show the layout
figure1

figure1 %<>%
  fill_panel(q1, column = 1, row = 1) %<>%
  fill_panel(q2, column = 2, row = 1) %<>%
  fill_panel(q3, column = 1, row = 2) %<>%
  fill_panel(q4, column = 2, row = 2)
#> `geom_smooth()` using method = 'loess' and formula 'y ~ x'
figure1

# complex layout
figure2 <- multi_panel_figure(columns = 3, rows = 3, panel_label_type = "upper-roman")

figure2 %<>%
  fill_panel(q1, column = 1:2, row = 1) %<>%
  fill_panel(q2, column = 3, row = 1) %<>%
  fill_panel(q3, column = 1, row = 2) %<>%
  fill_panel(q4, column = 2:3, row = 2:3)
#> `geom_smooth()` using method = 'loess' and formula 'y ~ x'
figure2

Created on 2018-07-06 by the reprex package (v0.2.0.9000).

Tung
  • 26,371
  • 7
  • 91
  • 115
12

Thanks to Andrie's comments and Harlan's answer to my previous question (!), I whipped up this solution which accomplishes what I was after:

set.seed(2)
q1 <- ggplot(data.frame(x=rnorm(50)), aes(x)) + geom_density()
q2 <- ggplot(data.frame(x=rnorm(50)), aes(x)) + geom_density()
q3 <- ggplot(data.frame(x=rnorm(50)), aes(x)) + geom_density()
q4 <- ggplot(data.frame(x=rnorm(50)), aes(x)) + geom_density()

grid.newpage()
pushViewport(viewport(layout=grid.layout(2,2)))
vplayout <- function(x,y) viewport(layout.pos.row=x,layout.pos.col=y)
print(q1,vp=vplayout(1,1))
print(q2,vp=vplayout(1,2))
print(q3,vp=vplayout(2,1))
print(q4,vp=vplayout(2,2))

which yields:

enter image description here

JD Long
  • 59,675
  • 58
  • 202
  • 294
4

A utility I think deserves more attention for this is wq::layOut (note the capital "O"). It is like base::layout in that the plots can be of varying sizes, laid out in rows and columns. Each argument to layOut is a 3-element list consisting of the plot, the row indices in which to plot it, and the column indices in which to plot it. For example:

library("ggplot2")
# Generate arbitrary ggplots
plot1 <- qplot(data = mtcars, x=wt, y=mpg, geom="point",main="Scatterplot of wt vs. mpg")
plot2 <- qplot(data = mtcars, x=wt, y=disp, geom="point",main="Scatterplot of wt vs disp")
plot3 <- qplot(wt,data=mtcars)
plot4 <- qplot(wt,mpg,data=mtcars,geom="boxplot")
plot5 <- qplot(wt,data=mtcars)
plot6 <- qplot(mpg,data=mtcars)
plot7 <- qplot(disp,data=mtcars)

wq::layOut(list(plot1, 1, 1),
           list(plot2, 1, 2),
           list(plot3, 2, 1),
           list(plot4, 2, 2),
           list(plot5, 3, 1:2),
           list(plot6, 4, 1:2),
           list(plot7, 1:2, 3))

enter image description here

Gregor Thomas
  • 136,190
  • 20
  • 167
  • 294