4

The following code used to work but doesn't anymore. Does anybody have an idea what's going on? It must be some change in the underlying gtable code.

require(cowplot) # for plot_grid()
require(grid) # for unit_max()

# make two plots
plot.iris <- ggplot(iris, aes(Sepal.Length, Sepal.Width)) + 
  geom_point() + facet_grid(. ~ Species) + stat_smooth(method = "lm") +
  background_grid(major = 'y', minor = "none") + 
  panel_border()
plot.mpg <- ggplot(mpg, aes(x = cty, y = hwy, colour = factor(cyl))) + 
  geom_point(size=2.5)

g.iris <- ggplotGrob(plot.iris) # convert to gtable
g.mpg <- ggplotGrob(plot.mpg) # convert to gtable

iris.widths <- g.iris$widths[1:3] # extract the first three widths, 
                                  # corresponding to left margin, y lab, and y axis
mpg.widths <- g.mpg$widths[1:3] # same for mpg plot
max.widths <- unit.pmax(iris.widths, mpg.widths) # calculate maximum widths
g.iris$widths[1:3] <- max.widths # assign max. widths to iris gtable
g.mpg$widths[1:3] <- max.widths # assign max widths to mpg gtable

plot_grid(g.iris, g.mpg, labels = "AUTO", ncol = 1)

The resulting plot is the following: enter image description here

What it should look like is this (with the y axis lines perfectly aligned vertically): enter image description here

The error seems to happen in this line:

g.iris$widths[1:3] <- max.widths

Any insight would be appreciated.

Note that similar solutions have been used for a long time, see e.g. here. And the plot_grid() function in cowplot uses code like this also to align plots, and it still works. So this has me completely mystified.

Community
  • 1
  • 1
Claus Wilke
  • 16,992
  • 7
  • 53
  • 104

1 Answers1

7

Edit
With grid version 3.3.0, this is no longer an issue. That is, the lines of code containing grid:::unit.list() below can be deleted.

The issue is to do with the way units are set. Look at g.iris$widths. You'll notice that the numerals are there but the units have been dropped. See this question and answer, and this one. After converting the plots to gtables, you need: g.iris$widths = grid:::unit.list(g.iris$widths)

require(grid) # for unit_max()
require(cowplot)

# make two plots
plot.iris <- ggplot(iris, aes(Sepal.Length, Sepal.Width)) + 
  geom_point() + facet_grid(. ~ Species) + stat_smooth(method = "lm") + 
  background_grid(major = 'y', minor = "none") + 
  panel_border()
plot.mpg <- ggplot(mpg, aes(x = cty, y = hwy, colour = factor(cyl))) + 
  geom_point(size=2.5) 

g.iris <- ggplotGrob(plot.iris) # convert to gtable   
g.mpg <- ggplotGrob(plot.mpg) # convert to gtable

g.iris$widths = grid:::unit.list(g.iris$widths)   
g.mpg$widths =  grid:::unit.list(g.mpg$widths)

iris.widths <- g.iris$widths[1:3] # extract the first three widths, 
                                  # corresponding to left margin, y lab, and y axis
mpg.widths <- g.mpg$widths[1:3] # same for mpg plot

max.widths <- unit.pmax(iris.widths, mpg.widths) # calculate maximum widths

g.iris$widths[1:3] <- max.widths # assign max. widths to iris gtable
g.mpg$widths[1:3] <- max.widths # assign max widths to mpg gtable

plot_grid(g.iris, g.mpg, labels = "AUTO", ncol = 1)
Community
  • 1
  • 1
Sandy Muspratt
  • 31,719
  • 12
  • 116
  • 122
  • 2
    indeed, `grid:::unit.list()` has been needed for some time; I forget when I started to notice breakage with ggplot2. This unexported grid function has become almost a requirement to do this kind of plot alignment, which may be problematic in a package. – baptiste Mar 06 '16 at 04:44
  • 1
    Thanks a lot! The axis lines issue is a bug in the latest ggplot2 version 2.1.0. It's already fixed in github with submission to CRAN pending. – Claus Wilke Mar 06 '16 at 04:51
  • I removed references to axis lines. – Sandy Muspratt Mar 06 '16 at 05:14
  • 1
    To be clear: The axis line bug is in ggplot2 2.1.0. I have fixed cowplot to work around the bug and submitted that to CRAN. – Claus Wilke Mar 06 '16 at 06:44
  • @baptiste Do you have any idea what can be done so that `grid:::unit.list()` becomes an exported function? If it is so important, it should be available. In fact, I was thinking about writing more helper functions to do these kinds of manipulations, but I'm not sure how to proceed now. – Claus Wilke Mar 06 '16 at 06:47
  • my personal take nowadays would be to put a copy of this function in my own package (I may do that actually).Thankfully there's nothing low-level / internal about this particular one, so it may be enough. You could also try your luck at reminding the `grid` (/gtable/ggplot2) developer(s) of this issue. – baptiste Mar 06 '16 at 07:00
  • 1
    @baptiste With the latest development version of R, this example should now work without `grid:::unit.list()`: https://stat.ethz.ch/pipermail/r-devel/2016-March/072434.html – Claus Wilke Mar 07 '16 at 23:11
  • @ClausWilke Thanks for tracking that down. – Sandy Muspratt Mar 07 '16 at 23:41