using ggsave and arrangeGrob after updating gridExtra to 2.0.0
Solution 1
Pascal brought me finally to the idea to check for the differences between ggplot 1.0.1
and ggplot 1.0.1.9003
, since I don't want to or force the development version of ggplot
.
So my idea is a function which will be executed within each script which overwrites the default ggsave
function.
I tested it now a little, if there are any bugs or so, please let me know. But the way I do it now it works so far.
repairGgsave <- function() {
ggplot_version <-
compareVersion(as.character(packageVersion('ggplot2')),
'1.0.1.9003')
gridextra_version <-
compareVersion(as.character(packageVersion('gridExtra')),
'0.9.1')
if(gridextra_version > 0) {
if(ggplot_version <= 0) {
ggsave <- function(filename, plot = last_plot(),
device = NULL, path = NULL, scale = 1,
width = NA, height = NA, units = c("in", "cm", "mm"),
dpi = 300, limitsize = TRUE, ...) {
dev <- plot_dev(device, filename, dpi = dpi)
dim <- plot_dim(c(width, height), scale = scale, units = units,
limitsize = limitsize)
if (!is.null(path)) {
filename <- file.path(path, filename)
}
dev(file = filename, width = dim[1], height = dim[2], ...)
on.exit(utils::capture.output(grDevices::dev.off()))
grid.draw(plot)
invisible()
}
assign("ggsave", ggsave, .GlobalEnv)
plot_dim <<- function(dim = c(NA, NA), scale = 1, units = c("in", "cm", "mm"),
limitsize = TRUE) {
units <- match.arg(units)
to_inches <- function(x) x / c(`in` = 1, cm = 2.54, mm = 2.54 * 10)[units]
from_inches <- function(x) x * c(`in` = 1, cm = 2.54, mm = 2.54 * 10)[units]
dim <- to_inches(dim) * scale
if (any(is.na(dim))) {
if (length(grDevices::dev.list()) == 0) {
default_dim <- c(7, 7)
} else {
default_dim <- dev.size() * scale
}
dim[is.na(dim)] <- default_dim[is.na(dim)]
dim_f <- prettyNum(from_inches(dim), digits = 3)
message("Saving ", dim_f[1], " x ", dim_f[2], " ", units, " image")
}
if (limitsize && any(dim >= 50)) {
stop("Dimensions exceed 50 inches (height and width are specified in '",
units, "' not pixels). If you're sure you a plot that big, use ",
"`limitsize = FALSE`.", call. = FALSE)
}
dim
}
plot_dev <<- function(device, filename, dpi = 300) {
if (is.function(device))
return(device)
eps <- function(...) {
grDevices::postscript(..., onefile = FALSE, horizontal = FALSE,
paper = "special")
}
devices <- list(
eps = eps,
ps = eps,
tex = function(...) grDevices::pictex(...),
pdf = function(..., version = "1.4") grDevices::pdf(..., version = version),
svg = function(...) grDevices::svg(...),
emf = function(...) grDevices::win.metafile(...),
wmf = function(...) grDevices::win.metafile(...),
png = function(...) grDevices::png(..., res = dpi, units = "in"),
jpg = function(...) grDevices::jpeg(..., res = dpi, units = "in"),
jpeg = function(...) grDevices::jpeg(..., res = dpi, units = "in"),
bmp = function(...) grDevices::bmp(..., res = dpi, units = "in"),
tiff = function(...) grDevices::tiff(..., res = dpi, units = "in")
)
if (is.null(device)) {
device <- tolower(tools::file_ext(filename))
}
if (!is.character(device) || length(device) != 1) {
stop("`device` must be NULL, a string or a function.", call. = FALSE)
}
dev <<- devices[[device]]
if (is.null(dev)) {
stop("Unknown graphics device '", device, "'", call. = FALSE)
}
dev
}
}
}
}
It basically overwrites the ggsave
and creates two new functions from the development version.
After executing the function everything seems to work.
Solution 2
As an temporary workaround for this unfortunate transition period, you could re-implement the class hack that used to be in gridExtra,
class(b) <- c("arrange","ggplot", class(b))
print.arrange <- function(x) grid.draw(x)
ggsave('test.pdf', b)
drmariod
Updated on June 14, 2022Comments
-
drmariod almost 2 years
since I read a lot similar question on stackoverflow so far, I couldn't find a good solution without updating ggplot2 to the development version.
My problem, I have several scripts which use
arrangeGrob
to create combined graph out of individual graphs. I save them into a variable andprint
this variable and/or save it withggsave
. Since a lot of my colleagues update there packages regularly (which is a good thing I think), I always get mails my script no longer work after updating togridExtra 2.0.0
.I am not sure how to handle this, since the new
ggplot2
version where the problem is solved is still under development. I found an article on stack overflow to remove a test if the object to save is aggplot
since the newarrangeGrob
function returns agtable
object, but this fails in my case:library(ggplot2) library(grid) library(gridExtra) a <- data.frame(x=c(1,2,3), y=c(2,3,4)) p <- ggplot(a, aes(x, y)) + geom_point() b <- arrangeGrob(p, p) grid.draw(b) ggsave('test.pdf', b) ggsave <- ggplot2::ggsave body(ggsave) <- body(ggplot2::ggsave)[-2] ggsave('test.pdf', b)
Some output and error on the console:
d> grid.draw(b) d> ggsave('test.pdf', b) Error in ggsave("test.pdf", b) : plot should be a ggplot2 plot d> ggsave <- ggplot2::ggsave d> body(ggsave) <- body(ggplot2::ggsave)[-2] d> ggsave('test.pdf', b) Saving 10.5 x 10.7 in image 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] d>
The
test.pdf
is created but it is corrupted in any way and can not be opened. Also thegtable
object get printed. So I guess something is wrong here.But, as you can see, I found in the example code, I found the
grid.draw
function to plot at least my combined graph but I still can notggsave
it after the modification.I don't want to use the "old" (
pdf(file = "test.pdf"); grid.draw(b); dev.off()
) device saving functions as suggested in this article, since they are very uncomfortable to use.In this question someone asked exactly how to save the object, but in the answer they just explain to use
grid.darw
and he accepted the answer assolving the problem
and nobody answered on my comments so far.So I am pretty lost at the moment, how to provide working scripts for those who have and have not updated to new
gridExtra
package. The way to remove the test within theggsave
function is I guess the best solution since I can check thegridExtra
andggplot2
version and just overwrite theggsave
function in case there version do not match, but I could not get it to run.Looking forward to get some help.
EDIT:
maybe the
sessionInfo
helpsd> sessionInfo() R version 3.2.0 (2015-04-16) Platform: x86_64-apple-darwin13.4.0 (64-bit) Running under: OS X 10.9.5 (Mavericks) locale: [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8 attached base packages: [1] grid stats graphics grDevices utils datasets methods base other attached packages: [1] gridExtra_2.0.0 ggplot2_1.0.1 loaded via a namespace (and not attached): [1] Rcpp_0.12.1 digest_0.6.8 MASS_7.3-44 plyr_1.8.3 gtable_0.1.2 [6] magrittr_1.5 scales_0.3.0 stringi_1.0-1 reshape2_1.4.1 devtools_1.9.1 [11] proto_0.3-10 tools_3.2.0 stringr_1.0.0 munsell_0.4.2 colorspace_1.2-6 [16] memoise_0.2.1
-
drmariod over 8 yearsThis
arrange
workaround has to be applied on plot I create witharrangeGrob
. Is there anything against the solution I suggested? In my case I only overwrite theggsave
function in case the versions ofgridExtra
andggplot
are not compatible. -
baptiste over 8 yearsit's good to have different options, I wouldn't say one is better in this case. Personally I prefer not to duplicate large chunks of code for temporary workarounds because it tends to confuse my future self, but everyone has a different workflow and/or capacity to remember the past.
-
drmariod over 8 yearsI just realised, if I use my function it no longer works for normal ggplot plots. It says
no applicable method for 'grid.draw' applied to an object of class "c('gg', 'ggplot')"
I wonder how this happens. -
Sander W. van der Laan almost 8 yearsThis seems relevant to me. Just one question: how should I apply it? Wrap around some ggsave-action?
repairGgsave(ggsave(paste0(PLOT_loc,"/",Today,"_ObsCoxQuint_CVD.eps"), plot = multiplot(ObsQuintPlot_CVD + guides(colour = FALSE), CoxQuintPlot_CVD + guides(colour = FALSE), cols = 2), width = 12, height = 12))
-
drmariod almost 8 years@SanderW.vanderLaan I actually never got it to work in a solid way. To be honest, I never really checked babtists solution above. I implemented the solution with
grid.new();grid.draw()
in my scripts. Thought it might be the best solution.