I am trying to use grid.arrange to display multiple graphs on the same page generated by ggplot. The plots use the same x data but with different y variables. The plots come
Edit
Simpler solutions are: 1) use the cowplot
package (see answer here); or 2) use egg
package available on github.
# devtools::install_github("baptiste/egg")
library(egg)
library(grid)
g = ggarrange(x1, x2, x3, x4, ncol = 2)
grid.newpage()
grid.draw(g)
Original
Minor edit: Updating code.
If you want to keep the axis labels, then with some fiddling, and borrowing code from here, this does the job.
library(ggplot2)
library(gtable)
library(grid)
library(gridExtra)
# Get the widths
gA <- ggplotGrob(x1)
gB <- ggplotGrob(x2)
gC <- ggplotGrob(x3)
gD <- ggplotGrob(x4)
maxWidth = unit.pmax(gA$widths[2:3], gB$widths[2:3],
gC$widths[2:3], gD$widths[2:3])
# Set the widths
gA$widths[2:3] <- maxWidth
gB$widths[2:3] <- maxWidth
gC$widths[2:3] <- maxWidth
gD$widths[2:3] <- maxWidth
# Arrange the four charts
grid.arrange(gA, gB, gC, gD, nrow=2)
ALTERNATIVE SOLUTIONS:
There are rbind
and cbind
functions in the gtable
package for combining grobs into one grob. For the charts here, the widths should be set using size = "max"
, but the CRAN version of gtable
throws an error.
One option is to examine the grid.arrange
plot, then use size = "first"
or size = "last"` options:
# Get the ggplot grobs
gA <- ggplotGrob(x1)
gB <- ggplotGrob(x2)
gC <- ggplotGrob(x3)
gD <- ggplotGrob(x4)
# Arrange the four charts
grid.arrange(gA, gB, gC, gD, nrow=2)
# Combine the plots
g = cbind(rbind(gA, gC, size = "last"), rbind(gB, gD, size = "last"), size = "first")
# draw it
grid.newpage()
grid.draw(g)
A second option is to bind
ing functions from gridExtra
package.
# Get the ggplot grobs
gA <- ggplotGrob(x1)
gB <- ggplotGrob(x2)
gC <- ggplotGrob(x3)
gD <- ggplotGrob(x4)
# Combine the plots
g = cbind.gtable(rbind.gtable(gA, gC, size = "max"), rbind.gtable(gB, gD, size = "max"), size = "max")
# Draw it
grid.newpage()
grid.draw(g)
That's exactly the kind of problem for which I wrote the cowplot package. It can be done in one line in that package:
require(cowplot) # loads ggplot2 as dependency
# re-create the four plots
A <- c(1,5,6,7,9)
B <- c(10,56,64,86,98)
C <- c(2001,3333,5678,4345,5345)
D <- c(13446,20336,24333,34345,42345)
L <- c(20,34,45,55,67)
M <- data.frame(L, A, B, C, D)
x1 <- ggplot(M, aes(L, A,xmin=10,ymin=0)) + geom_point() + stat_smooth(method='lm')
x2 <- ggplot(M, aes(L, B,xmin=10,ymin=0)) + geom_point() + stat_smooth(method='lm')
x3 <- ggplot(M, aes(L, C,xmin=10,ymin=0)) + geom_point() + stat_smooth(method='lm')
x4 <- ggplot(M, aes(L, D,xmin=10,ymin=0)) + geom_point() + stat_smooth(method='lm')
# arrange into grid and align
plot_grid(x1, x2, x3, x4, align='vh')
This is the result: (Note that cowplot changes the default ggplot2 theme. You can get the gray one back though if you really want to.)
As a bonus feature, you can also add plot labels in the top-left corner of each graph:
plot_grid(x1, x2, x3, x4, align='vh', labels=c('A', 'B', 'C', 'D'))
Result:
I use the labels
option on virtually every multi-part graph I make.
If you are using RMarkdown and knitting to PDF, I have an alternative approach. Knitr offers the functionality to plot subfigures when creating a PDF, which allows you to have multiple figures in a plot, each with their own caption.
For this to work, each plot has to be displayed separately. By joining together several functions from the cowplot
package, I made the following function which aligns plots whilst keeping them as separate objects:
plot_grid_split <- function(..., align = "hv", axis= "tblr"){
aligned_plots <- cowplot::align_plots(..., align=align, axis=axis)
plots <- lapply(1:length(aligned_plots), function(x){
cowplot::ggdraw(aligned_plots[[x]])
})
invisible(capture.output(plots))
}
Here is an example, comparing the layout normally vs using the function:
---
output: pdf_document
header-includes:
- \usepackage{subfig}
---
```{r}
plot_grid_split <- function(..., align = "hv", axis= "tblr"){
aligned_plots <- cowplot::align_plots(..., align=align, axis=axis)
plots <- lapply(1:length(aligned_plots), function(x){
cowplot::ggdraw(aligned_plots[[x]])
})
invisible(capture.output(plots))
}
```
```{r fig-sub, fig.cap='Four Plots Not Aligned', fig.subcap=c('Plot One', 'Plot Two', 'Plot Three', 'Plot Four'), out.width='.49\\linewidth', fig.asp=1, fig.ncol = 2}
library(ggplot2)
plot <- ggplot(iris, aes(Sepal.Length, Sepal.Width, colour = Species)) +
geom_point()
plot + labs(title = "A nice title")
plot + labs(caption = "A sample caption")
plot + theme(legend.position = "none")
plot + theme(legend.position = "top")
```
```{r fig-sub-2, fig.cap='Four Plots Aligned', fig.subcap=c('Plot One', 'Plot Two', 'Plot Three', 'Plot Four'), out.width='.49\\linewidth', fig.asp=1, fig.ncol = 2}
x1 <- plot + labs(title = "A nice title")
x2 <- plot + labs(caption = "A sample caption")
x3 <- plot + theme(legend.position = "none")
x4 <- plot + theme(legend.position = "top")
plot_grid_split(x1, x2, x3, x4)
```
- You can learn more about subfigures in R within this post.
- In addition, you can check out the knitr options to read more about the chunk options for subfigures: https://yihui.name/knitr/options/
Patchwork is a new package which makes it really easy to format and layout multiple ggplots. One of the best things about it is that it aligns the plot areas automatically. Plus, the syntax is really easy.
devtools::install_github("thomasp85/patchwork")
library(patchwork)
x1 + x2 + x3 + x4 + plot_layout(ncol = 2)
Check out the GitHub page for more examples: https://github.com/thomasp85/patchwork
I would use faceting for this problem:
library(reshape2)
dat <- melt(M,"L") # When in doubt, melt!
ggplot(dat, aes(L,value)) +
geom_point() +
stat_smooth(method="lm") +
facet_wrap(~variable,ncol=2,scales="free")
Note: The layman may miss that the scales are different between facets.