Store output from gridExtra::grid.arrange into an object - r

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

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)
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)

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.

Related

R: Suppress base graphics plot but return plot as object

I have a function that both returns some statistics and makes a plot using R base graphics. I want to suppress the plotting from that function and instead return the plot as an object, so that plotting or not can be controlled from outside the function.
I've tried:
Using the gridGraphics package I can convert a base graphics plot to an object as suggested in this question:
plot(1:10)
grid.echo()
a = grid.grab()
grid.draw(a)
The remaining problem is that plot() command draws the plot that I want to suppress. Thus I tried to suppress it by plotting to a device in a temp file like answer here suggests. Code becomes:
ff = tempfile()
svg(filename = ff)
plot(1:10)
grid.echo()
a = grid.grab()
dev.off()
unlink(ff)
but now, grid.echo() can not find any graphics to echo, and throws warning:
Warning message:
In grid.echo.recordedplot(recordPlot(), newpage, prefix, device) :
No graphics to replay
I've traced this problem back to grid.echo() not being able to echo graphics from other devices than the Rstudio default device, regardless of being in a temp file or not. This is in itself strange, since grid.echo() docs says it echoes graphics from the current device, not mentioning any restrictions.
Can anyone help me to solve the problem of suppressing base graphics plot and returning it as object, either by suggesting how to fix my broken attempt, or with another approach?
LocoGris solved the problem in this related question about the behaviour of grid.echo.
The following code will plot the unwanted graphics output into a tempfile, save the plot as a grid object using grid.echo and grid.grab before unlinking the tempfile so that only the plot object remains, thereby producing the besired behaviour:
ff = tempfile()
svg(filename = ff)
plotfun <- function() plot(1:10)
grid.echo(plotfun)
a = grid.grab()
dev.off()
unlink(ff)
The difference to code in question is that the plotting in R base graphics is put into a function and passed to grid.echo, instead of relying on grid.echo to automatically find what it needs from the current device.
Potentially useful note: grid.echo will plot two times, which can be seen if using regular svg() without tempfiles, as two files will appear with the same plot. I assume that the first plot is in R base graphics and the second one is the echo using the grid system.

Loading an R package without all the names in it

I'm sorry if this a duplicate, but it's un-internet-searchable.
I would like to load ggplot, but without littering my global namespace with all the functions therein.
I am content with accessing the functions as ggplot2::aes(), and I don't want just aes() to work.
In other programming languages, this is the default behaviour.
I think you have really answered this yourself. This works without any library statements as long as ggplot2 is installed:
ggplot2::ggplot(BOD, ggplot2::aes(Time, demand)) + ggplot2::geom_point()
This could alternately be done like this:
ggplot <- ggplot2::ggplot
aes <- ggplot2::aes
geom_point <- ggplot2::geom_point
ggplot(BOD, aes(Time, demand)) + geom_point()
Another possibility is to just temporarily add ggplot2 and then remove it again:
library(ggplot2)
ggplot(BOD, aes(Time, demand)) + geom_point()
detach("package:ggplot2", unload = TRUE)
There are a number of R packages which provide facilities that may be of interest including the modules and import packages on CRAN. Also the klmr modules R package on github (not on CRAN) provides a Python-like framework.

In R, how do I save a data.tree plot to a file?

I'm unable to save a plot generated by the plot.Node function in data.tree. I've tried the following:
### Create tree object and plot it
data(acme);
plot(acme);
This works fine, showing the plot, as one would expect.
### Try saving it as png
png(filename='file.png', type='cairo-png');
plot(acme);
dev.off();
This creates an empty file. ggsave does the same. Apparantly, plot.Node uses DiagrammeR under the hood, so I looked into that package. It has a function to export graphs:
export_graph(acme, file_name="file.png");
This gives the error:
Error in file.exists(diagram) : invalid 'file' argument
When I transform to GraphViz first, I get a different error:
export_graph(ToGraphViz(acme), file_name="file.png");
Error in graph$dot_code : $ operator is invalid for atomic vectors
Clearly, exporting to GraphViz doesn't quite export to what DiagrammeR expects.
I'm in RStudio and so could in theory just save the plot using the GUI, but I need this for in a script.
Apparently, plot.Node doesn't actually plot anything - instead it seems to generate html/js. Does this mean that that result cannot be stored as a graphic? Or is there some export/conversion function somewhere that I'm completely missing? it certainly feels like I'm missing something obvious - I assume the need to store plotted data.trees as images is quite common. But I have no idea which potential solutions I can still explore.
I'd be very grateful for any pointers anybody has!
As sebastian-c suggested, things work now a bit differently than suggested by Matherion, as of R 3.3.3 with data.tree 0.7.0 and DiagrammeR 0.9.0
Pre-requisite: DiagrmmeRsvg and dependencies need to be installed. Depending on your OS, for this to work you might have to install V8. For example on ubuntu:
apt-get install libv8-3.14-delibv8-3.14-dev
And then in R:
install.packages("DiagrammeRsvg")
On Windows, I didn't have to install anything (maybe because Chrome is installed?).
Once DiagrammeRsvg is available, run:
library(data.tree)
data(acme)
library(DiagrammeR)
export_graph(ToDiagrammeRGraph(acme), "export.pdf")
I've found the answer, at least, partially. There exists a package dedicated to exporting GraphViz diagrams to SVG: DiagrammeRsvg.
So this works:
treeAsSVG <- export_svg(grViz(ToGraphViz(acme)));
writeLines(treeAsSVG, "filename.svg"));
The grViz is necessary to actually convert the ToGRaphViz output to something that can be interpreted by export_svg. I'm still not really sure (yet) what all goes on under the hood - for example, this does not work:
export_graph(grViz(ToGraphViz(acme)), file_name="filename.svg");
But, if anybody else has a similar problem and stumbles upon this question, perhaps this partial answer can help them to at least export something that can then be integrated in e.g. html pages.
By converting acme with as.phylo() it works, but it looks a little boring:
plot(as.phylo(acme),
show.node.label=TRUE,
node.pos=2,
no.margin=TRUE
)
# adding edge labels
edgelabels(as.vector(acme$Get("cost"))[-1],
adj = c(0,-0.5),
frame = "none")
solution with as.phylo()
I also tried as.igraph. However, the nodes overlap and it looks even less pretty:
plot(0, type="n", ann=FALSE, axes=FALSE,
xlim=extendrange(ig[,1]),
ylim=extendrange(ig[,2]))
plot(ig,
layout=layout_as_tree(ig,root=1),
vertex.shape="rectangle",
vertex.size=(strwidth(V(ig)$name) + strwidth("oo")) * 100,
#vertex.size2=strheight("I") * 2 * 100,
edge.label=acme$Get("p",traversal = "level")[-1]
)
solution with as.igraph()

R programming Graph plot not appearing ggplot2 [duplicate]

Let's assume I have 2 source files, the first one named example1.r and the second one example2.r (given below).
example1.r
plot(1:10,1:10)
example2.r
qplot(1:10,1:10)
When I source example1.r, the graph is drawn. It does not, however, when I source example2.r. What is the solution here?
(qplot in example2.r is ggplot2's function)
Update:
.R files: source's option print.eval=TRUE will lead to printing behaviour of the evaluation result like in the interactive command line.
source("Script.R", print.eval=TRUE)
.Rnw files: knitr by default emulates the behaviour of the interactive command line wrt. printing. Note that knitr can be specified as Sweaving engine also for R package vignettes.
This is my original answer. But note that this workaround is IMHO completely obsolete now (and it always was good for a small lazy niche only).
This is the famous FAQ 7.22: Why do lattice/trellis graphics not work?.
For grid graphics like ggplot2 or lattice, you need to print the graphics object in order to actually draw it.
Interactively on the command line this is done automatically. Everywhere else (inside files to be sourced, loops, functions, Sweave chunks) you need to print it explicitly.
print (qplot (1 : 10, 1 : 10))
Alternatively, you can redefine qplot to do the printing:
qplot <- function (x, y = NULL, z = NULL, ...) {
p <- ggplot2::qplot (x = x, y = y, z = z, ...)
print (p)
}
(this changes the axis labels to x and y).
I use this approach in vignettes where I want to write code exactly as a user in an interactive session would type it.

Creating a custom Stat object in ggplot2

I'd like to create a custom Stat object for ggplot2. (Specifically I'd like to create a smoother that works differently than the ones stat_smooth allows- for instance, without a y~x modeling function- but there are other custom Stats I'd like to create even if there were a workaround for my specific case).
I found this suggested solution from Hadley Wickham:
StatExpo <- proto(Stat, {
objname <- "expo"
desc <- "Exponential smoothing"
default_geom <- function(.) GeomLine
calculate_groups <- function(., data, scales, variable="x", ...) {
data$y <- HoltWinters(data$x, ...)
}
})
stat_expo <- StatExpo$new
However, when I try it I get:
Error in proto(Stat, { : object 'Stat' not found
Upon looking around the ggplot code, I found where Stat is defined. However, the Stat object is, as far as I can tell, never exported from ggplot2.
I could write my new stat object within the ggplot2/R folder and then reinstall the package, but obviously this would be cumbersome and make the solution very difficult to share with others. How can I create a custom Stat object outside of the ggplot namespace?
ggplot2:::Stat can be used to access the non-exported object.
getFromNamespace('Stat','ggplot2')

Resources