Left align two graph edges (ggplot)

RGgplot2GridextraGtable

R Problem Overview


I'm using ggplot and have two graphs that I want to display on top of each other. I used grid.arrange from gridExtra to stack them. The problem is I want the left edges of the graphs to align as well as the right edges regardless of axis labels. (the problem arises because the labels of one graph are short while the other is long).

The Question:
How can I do this? I am not married to grid.arrange but the ggplot2 is a must.

What I've tried:
I tried playing with widths and heights as well as ncol and nrow to make a 2 x 2 grid and place the visuals in opposite corners and then play with the widths but I couldn't get the visuals in opposite corners.

require(ggplot2);require(gridExtra)
A <- ggplot(CO2, aes(x=Plant)) + geom_bar() +coord_flip() 
B <- ggplot(CO2, aes(x=Type)) + geom_bar() +coord_flip() 
grid.arrange(A, B, ncol=1)

enter image description here

R Solutions


Solution 1 - R

Try this,

 gA <- ggplotGrob(A)
 gB <- ggplotGrob(B)
 maxWidth = grid::unit.pmax(gA$widths[2:5], gB$widths[2:5])
 gA$widths[2:5] <- as.list(maxWidth)
 gB$widths[2:5] <- as.list(maxWidth)
 grid.arrange(gA, gB, ncol=1)

Edit

Here's a more general solution (works with any number of plots) using a modified version of rbind.gtable included in gridExtra

gA <- ggplotGrob(A)
gB <- ggplotGrob(B)
grid::grid.newpage()
grid::grid.draw(rbind(gA, gB))

Solution 2 - R

I wanted to generalize this for any number of plots. Here is a step-by-step solution using the approach by Baptiste:

plots <- list(A, B, C, D)
grobs <- list()
widths <- list()

collect the widths for each grob of each plot

for (i in 1:length(plots)){
    grobs[[i]] <- ggplotGrob(plots[[i]])
    widths[[i]] <- grobs[[i]]$widths[2:5]
}

use do.call to get the max width

maxwidth <- do.call(grid::unit.pmax, widths)

asign the max width to each grob

for (i in 1:length(grobs)){
     grobs[[i]]$widths[2:5] <- as.list(maxwidth)
}

plot

do.call("grid.arrange", c(grobs, ncol = 1))

Solution 3 - R

Using cowplot package:

A <- ggplot(CO2, aes(x=Plant)) + geom_bar() +coord_flip() 
B <- ggplot(CO2, aes(x=Type)) + geom_bar() +coord_flip() 

library(cowplot)
plot_grid(A, B, ncol=1, align="v")

enter image description here

Solution 4 - R

On http://rpubs.com/MarkusLoew/13295 is a really easy solution available (last item) Applied to this problem:

require(ggplot2);require(gridExtra)
A <- ggplot(CO2, aes(x=Plant)) + geom_bar() +coord_flip() 
B <- ggplot(CO2, aes(x=Type)) + geom_bar() +coord_flip() 
grid.draw(rbind(ggplotGrob(A), ggplotGrob(B), size="first"))

you can also use this for both width and height:

require(ggplot2);require(gridExtra)
A <- ggplot(CO2, aes(x=Plant)) + geom_bar() +coord_flip() 
B <- ggplot(CO2, aes(x=Type)) + geom_bar() +coord_flip() 
C <- ggplot(CO2, aes(x=conc)) + geom_bar() +coord_flip()
D <- ggplot(CO2, aes(x=uptake)) + geom_bar() +coord_flip() 
grid.draw(cbind(
            rbind(ggplotGrob(A), ggplotGrob(B), size="first"),
            rbind(ggplotGrob(C), ggplotGrob(D), size="first"),
            size='first'))

Solution 5 - R

The egg package wraps ggplot objects into a standardised 3x3 gtable, enabling the alignment of plot panels between arbitrary ggplots, including facetted ones.

library(egg) # devtools::install_github('baptiste/egg')
library(ggplot2)

p1 <- ggplot(mtcars, aes(mpg, wt, colour = factor(cyl))) +
  geom_point() 

p2 <- ggplot(mtcars, aes(mpg, wt, colour = factor(cyl))) +
  geom_point() + facet_wrap( ~ cyl, ncol=2, scales = "free") +
  guides(colour="none") +
  theme()

ggarrange(p1, p2)

enter image description here

Solution 6 - R

Here is another possible solution using melt from the reshape2 package, and facet_wrap:

library(ggplot2)
library(reshape2)

dat = CO2[, c(1, 2)]
dat$id = seq(nrow(dat))
mdat = melt(dat, id.vars="id")

head(mdat)
#   id variable value
# 1  1    Plant   Qn1
# 2  2    Plant   Qn1
# 3  3    Plant   Qn1
# 4  4    Plant   Qn1
# 5  5    Plant   Qn1
# 6  6    Plant   Qn1

plot_1 = ggplot(mdat, aes(x=value)) + 
         geom_bar() + 
         coord_flip() +
         facet_wrap(~ variable, nrow=2, scales="free", drop=TRUE)

ggsave(plot=plot_1, filename="plot_1.png", height=4, width=6)

enter image description here

Solution 7 - R

The patchwork package handles this by default:

library(ggplot2)
library(patchwork)

A <- ggplot(CO2, aes(x = Plant)) + geom_bar() + coord_flip() 
B <- ggplot(CO2, aes(x = Type)) + geom_bar() + coord_flip() 

A / B

Created on 2019-12-08 by the reprex package (v0.3.0)

Solution 8 - R

I know this is an old post, and that it has already been answered, but may I suggest combining @baptiste's approach with purrr to make it nicer-looking:

library(purrr)
list(A, B) %>% 
  map(ggplotGrob) %>% 
  do.call(gridExtra::gtable_rbind, .) %>% 
  grid::grid.draw()

Solution 9 - R

At best this is a hack:

library(wq)
layOut(list(A, 1, 2:16),  list(B, 2:3, 1:16))

It feels really wrong though.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionTyler RinkerView Question on Stackoverflow
Solution 1 - RbaptisteView Answer on Stackoverflow
Solution 2 - RslizbView Answer on Stackoverflow
Solution 3 - Rzx8754View Answer on Stackoverflow
Solution 4 - RWilbertView Answer on Stackoverflow
Solution 5 - RbaptisteView Answer on Stackoverflow
Solution 6 - RbdemarestView Answer on Stackoverflow
Solution 7 - RMSRView Answer on Stackoverflow
Solution 8 - RFelipe GerardView Answer on Stackoverflow
Solution 9 - RTyler RinkerView Answer on Stackoverflow