Align plot areas in ggplot
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 out with differing dimensions due to the y-data having different scales.
I have tried using various theme options within ggplot2 to change the plot size and move the y axis label but none have worked to align the plots. I want the plots arranged in a 2 x 2 square so that each plot is the same size and the x-axes align.
Here is some test data:
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)
And the code that I am using to plot:
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')
grid.arrange(x1,x2,x3,x4,nrow=2)
If you run this code, you will see that the bottom two plots have a smaller plot area due to the greater length of the y-axes units.
How do I make the actual plot windows the same?
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.
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.
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