I'm trying to match the width of the plotting area of two plots arranged vertically (the range of data on both plots is the same). I tried putting them together using gtable and then manipulating margins to make adjustments. It seems that, since the bottom plot has no axis ticks or labels, setting the same margins makes the plots slightly misaligned. It is not clear to me how the total width of the area to the left of the plot region is calculated. It seems that the width of the text labels is somehow added to it and I have no way of working out how wide that is. (I could match the widths by experimentation but I need to generate many plots like these and I would like the solution to work on arbitrary data.) I experimented with various combinations of plot.margin
, panel.margin
, the margin
property of axis.text.y
and the (deprecated) axis.ticks.margin.y
but to no avail. Here's a minimal working example:
library(ggplot2)
library(grid)
library(gtable)
ex <- data.frame(xmin=1, xmax=100, ymin=0, ymax=15)
p <- ggplot(ex, aes(xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax)) +
theme_minimal() +
theme(plot.margin=margin(l=2.0, unit="cm"),
panel.margin=margin(l=2.0, r=0, unit="cm"),
panel.grid.major.x=element_blank(), panel.grid.minor.x=element_blank(),
panel.border=element_blank(), panel.background = element_blank(),
axis.text.y=element_text(margin=margin(l=0.0, r=0.0, unit="cm")),
axis.ticks=element_line(size = 0.25),
axis.ticks.x=element_line(), axis.ticks.y=element_line(),
axis.ticks.length=unit(0.1, units="cm")) +
scale_x_continuous(expand = c(0, 0)) +
scale_y_continuous(expand = c(0, 0)) +
geom_rect()
ex_ann <- data.frame(xmin=10, xmax=50, ymin=0, ymax=1)
ann <- ggplot(ex_ann, aes(xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax)) +
theme_minimal() +
theme(plot.margin=margin(l=2.0, unit="cm"),
panel.margin=margin(l=2.0, unit="cm"),
panel.background=element_rect(fill="grey", colour="white"),
panel.grid.major.x=element_blank(), panel.grid.minor.x=element_blank(),
panel.grid.major.y=element_blank(), panel.grid.minor.y=element_blank(),
panel.border = element_blank(), panel.background = element_blank(),
# axis.text.y=element_text(margin=margin(r=0, l=0.5)), panel.margin=unit(0, units="cm"),
axis.ticks=element_line(size = 0.25),
axis.ticks.x=element_line(), axis.ticks.y=element_blank(),
axis.text.y=element_blank(),
axis.ticks.length=unit(0.0, units="cm")) +
scale_x_continuous(limits=c(1, 100), expand = c(0, 0)) +
scale_y_continuous(limits=c(0, 1), expand = c(0, 0)) +
geom_rect()
gr <- ggplotGrob(p)
gr_ann <- ggplotGrob(ann)
g_joint <- rbind(gr, gr_ann, size="first") # stack the two plots
g_joint$widths <- unit.pmax(gr$widths, gr_ann$widths) # use the largest widths
g_joint$layout[grepl("guide", g_joint$layout$name),c("t","b")] <- c(1,nrow(g_joint))
grid.newpage()
grid.draw(g_joint)
g <- gtable(widths=unit(c(1), "null"), heights=unit(c(1, 0.1), "null"))
g <- gtable_add_grob(g, list(gr, gr_ann), t=c(1, 2), l=c(1, 1), r=c(1, 1), b=c(1, 2))
grid.newpage()
grid.draw(g)
I found this solution online: https://github.com/hadley/ggplot2/wiki/Align-two-plots-on-a-page. It works in simple examples but my actual plots become garbled. I don't understand enough knowledge of the ggplot2 internals to work out what the problem is. Another limitation of this solution is that in the example both plots have the same height and I would like the bottom plot to have height of about 1/10th of the top one.