12

I am placing multiple plots into one image using gridExtra::grid.arrange and would like to have the option of saving the combined plot as an object that could be returned from within a function as part of a list of returned objects. Ideally, I would like to do this without printing the plot object.

The code below creates two plots, combines them with grid.arrange, and attempts to save the result into x. However, x evaluates to NULL and the plot is printed. The documentation for grid.arrange points me to arrangeGrob and suggests plotting can be turned off using plot=FALSE, but I get an error when I try that because FALSE is not a grob object.

Any suggestions for what I'm not understanding?

# R under development
# Windows 7 (32 bit)
# ggplot2 1.0.0
# gridExtra 0.9.1

p1 <- ggplot(mtcars, aes(x=factor(cyl), y=mpg)) + geom_boxplot()

p2 <- ggplot(mtcars, aes(x=factor(cyl), y=wt)) + geom_boxplot()

x <- gridExtra::grid.arrange(p1, p2)

x

Per the comments, I'm adding this edit. When I try it with arrangeGrob, I get no output at all.

> gridExtra::arrangeGrob(p1, p2)
> print(gridExtra::arrangeGrob(p1, p2))
Error: No layers in plot
> x <- gridExtra::arrangeGrob(p1, p2)
> x
Error: No layers in plot
Benjamin
  • 16,897
  • 6
  • 45
  • 65
  • 6
    Try using `arrangeGrob` instead – hrbrmstr Mar 15 '15 at 16:24
  • 2
    @hrbrmstr: I think a small answer would be worthwhile. The help page is rather unhelpful in this instance and this was not something I would have been able to answer off the top of my head despite a fair bit of experience. – IRTFM Mar 15 '15 at 18:40
  • It actually gets weirder with `arrangeGrob` (I keep forgetting that code doesn't format in the comments, so I made an edit to the question). – Benjamin Mar 16 '15 at 00:56

2 Answers2

6

The code in your edit does not work properly since you didn't load gridExtra.

library(gridExtra)
y <- arrangeGrob(p1, p2, ncol = 1)
class(y)
#[1] "gtable" "grob"   "gDesc"
grid.draw(y)

enter image description here

Edit: since version 2.0.0, my comment about grid dependency below is no longer valid, since grid is now imported.

Edit: With gridExtra version >= 2.0.0, there is no need to attach either package,

p <- ggplot2::qplot(1,1)
x <- gridExtra::arrangeGrob(p, p)
grid::grid.draw(x)
tonytonov
  • 25,060
  • 16
  • 82
  • 98
  • Interesting. That isn't behavior I would have expected. Perhaps I don't understand the `::` operator as well as I thought. Thanks for setting me straight. – Benjamin Mar 16 '15 at 12:31
  • Sorry, after loading `gridExtra` you don't need `gridExtra::`, see the edit. You are welcome! – tonytonov Mar 16 '15 at 13:26
  • That I don't need `gridExtra::` after loading the package I understood. I was using the `::` operator because I have this function in a package and am trying to avoid loading additional packages into the search path when loading my package. I had thought that loading the library or using `::` would produce identical results. Apparently that isn't the case here. – Benjamin Mar 16 '15 at 13:47
  • I see. The recommended course of action (you're probably aware of that, sorry if that's trivial) is to put `gridExtra` in `Imports` and use `importFrom` for the function you need, `arrangeGrob` in your case. – tonytonov Mar 16 '15 at 14:13
  • Yup, I've got all that. Still doesn't work. I think it might be how `gridExtra` is set up. I just realized that I shouldn't have to use `library(ggplot2)` to make my function work, because I've imported the library in my NAMESPACE. But R complains that it can't find `ggplotGrob` (a `ggplot2` function) when it executes `arrangeGrob`. Looking at the DESCRIPTION file for `gridExtra` shows it only suggests `ggplot2`, so my guess is that `gridExtra` can't find `ggplot2` on the search path unless the user loads it him or herself. Unless `gridExtra` is updated, I think I'll have to load it. – Benjamin Mar 16 '15 at 14:51
  • 1
    Ah, I remember now. `gridExtra` _depends_ on `grid`, so if you import `gridExtra`, it won't work properly since it won't find `grid`. Quite an old style of writing packages, unfortunately. Link [here](http://stackoverflow.com/questions/8637993/better-explanation-of-when-to-use-imports-depends/8638902#8638902), see "caveat" section. – tonytonov Mar 16 '15 at 15:50
  • Related: http://r2d2.quartzbio.com/posts/package-depends-dirty-hack-solution.html – tonytonov Mar 16 '15 at 16:06
  • This is weird. I'm using exactly this answer and I get this: ```> class(y) [1] "gtable" "grob" "gDesc" > y TableGrob (2 x 1) "arrange": 2 grobs z cells name grob 1 1 (1-1,1-1) arrange gtable[layout] 2 2 (2-2,1-1) arrange gtable[layout] ``` What's going on here? – maxheld Jul 16 '15 at 14:58
  • this answer does not work for me, strangely. I've posted a *new* question to that effect: http://stackoverflow.com/questions/31458051/store-arrangegrob-to-object-does-not-create-printable-object – maxheld Jul 16 '15 at 15:10
  • problem solved: http://stackoverflow.com/questions/31458051/store-arrangegrob-to-object-does-not-create-printable-object/31458190#31458190 Notice that `gridExtra` was recently updated: result can now only be `plot()`ed, not `print()`ed. – maxheld Jul 16 '15 at 15:19
  • 2
    please don't use `plot()`, the output is a gtable, and should be drawn with `grid.draw()` (following grid conventions). – baptiste Aug 01 '15 at 23:33
3

Funny that this was asked so recently - I was running into this problem as well this week and was able to solve it in a bit of a hacky way, but I couldn't find any other solution I was happier with.

Problem 1: ggplotGrob is not found

I had to make sure ggplot2 is loaded. I don't completely understand what's happening (I admit I don't fully understand imports/depends/attaching/etc), but the following fixes that. I'd be open to feedback if this is very dangerous.

if (!"package:ggplot2" %in% search()) {
  suppressPackageStartupMessages(attachNamespace("ggplot2"))
  on.exit(detach("package:ggplot2"))
}

Somebody else linked to this blog post and I think that works as well, but from my (non-complete) understanding, this solution is less horrible. I think.

Problem 2: no layers in plot

As you discovered too, fixing that problem allows us to use grid.arrange, but that returns NULL and doesn't allow saving to an object. So I also wanted to use arrangeGrob but I also ran into the above error when gridExtra was not already loaded. Applying the fix from problem 1 again doesn't seem to work (maybe the package is getting de-attached too early?). BUT I noticed that calling grid::grid.draw on the result of arrangeGrob prints it fine without error. So I added a custom class to the output of arrangeGrob and added a generic print method that simply calls grid.draw

f <- function() {
  plot <- gridExtra::arrangeGrob(...)
  class(plot) <- c("ggExtraPlot", class(plot))
  plot
}
print.ggExtraPlot <- function(x, ...) {
  grid::grid.draw(x)
}

Hooray, now I can open a fresh R session with no packages explicitly loaded, and I can successfully call a function that creates a grob and print it later!


You can see the code in action in my package on GitHub.

DeanAttali
  • 25,268
  • 10
  • 92
  • 118