Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

equalizing ggplot2 panel heights in a stacked plot with arrangeGrob

I've got two sets of time-series data that I want to plot in a stacked arrangement. So far I've been able to come up with something like this:

library(ggplot2);
library(gridExtra);

t=1:100; s=sin(t/10); c=cos(t/10);
g1=ggplot()+theme_bw()+geom_line(aes(x=t,y=s))+ylab(NULL)
g2=ggplot()+theme_bw()+geom_line(aes(x=t,y=c))+ylab("Cosine")+xlab("Time")

# get rid of the top plot's axis labels
g1=g1+theme(
  axis.text.x=element_blank(),
  panel.margin = unit(0,"null")
  );
g1=g1+labs(x=NULL);

# zero bottom margin of top plot
g1$theme$plot.margin[3]=unit(0,"null");

# zero top margin of bottom plot
g2$theme$plot.margin[1]=unit(0,"null");

# this trick equalizes the width of the two plot panels
g1g=ggplotGrob(g1);
g2g=ggplotGrob(g2);
g1g$widths=g2g$widths

# however, equalizing the heights of the panels is not so simple as
# the following:
# g1g$heights=g2g$heights

g=arrangeGrob(g1g,g2g)

plot(g)   #ggsave("out.svg",g,width=5,height=1.5);

The combined plot is shown below. I made it particularly wide and short so that you could see the problem: arrangeGrob equalizes the plot heights, but doing so makes the plot panels have different heights. The bottom panel gets squished by the x axis label and tick labels on the bottom plot, which the top plot lacks.

combined plot

Now, I could recycle the trick I used to equalize the widths. Uncommenting the line

g1g$heights=g2g$heights

yields the following result:

fix height attempt plot

This is not what I want because of the excessive vertical space which now appears between the plots - I had wanted them to be touching.

I know that I could pass a heights argument to arrangeGrob, to specify the relative heights of the plots:

g=arrangeGrob(g1g,g2g,heights=c(1,2))

But then I have to fiddle with the numbers until it looks right.

I'm wondering if there's an easy way to automatically force just the two panels to have the same height when the final grob is rendered.

like image 340
Metamorphic Avatar asked Feb 08 '23 13:02

Metamorphic


1 Answers

use rbind instead,

grid.draw(rbind(ggplotGrob(g1), ggplotGrob(g2)))

enter image description here

and if you want to get rid off the space in-between, it's easier to remove those rows from the gtable than to mess with the plot margins (your manual changes to the theme settings produced an error so I ignored those lines).

grid.newpage()
grid.draw(rbind(ggplotGrob(g1)[-(4:6),], ggplotGrob(g2)[-(1:2),]))

enter image description here

Changing the panel heights has to be done in a separate step, e.g. using this little helper function

g12 <- rbind(ggplotGrob(g1)[-(4:6),], ggplotGrob(g2)[-(1:2),])

resize_heights <- function(g, heights = rep(1, length(idpanels))){
  idpanels <- unique(g$layout[grepl("panel",g$layout$name), "t"])
  g$heights <- grid:::unit.list(g$heights)
  g$heights[idpanels] <- unit.c(do.call(unit, list(heights, 'null')))
  g
}

grid.newpage()
grid.draw(resize_heights(g12, c(3,1)))

enter image description here

like image 128
baptiste Avatar answered Mar 04 '23 00:03

baptiste