I'm creating bunch of tests for ggplots. I included things like checking labels, if the output object is ggplot etc. But I have no idea how to test if the plot was saved using testthat and ggsave. Do you have any idea how it can be done ?
Here is a way to test based on file size. If the file doesn't exist it is NA and if it does exist it should be > 0.
library(testthat)
library(ggplot2)
test_that("plot is saved to file", {
file <- tempfile(fileext = ".png")
expect_equal(file.size(file), NA_real_)
plot <- ggplot(mtcars, aes(wt, mpg)) +
geom_point()
ggsave(file, plot, "png")
expect_true(file.size(file) > 0)
unlink(file)
})
I created a function to automate creating ggplot column plots. However when I call the function it outputs a list:
Plotter<- function (df,title){
Plots <-ggplot2(df,aes(x=mpg,y=wt))+geom_col()+ggtitle(title)
print(Plots)
return(Plots)
}
plot1 <- Plotter(data,"test")
plot2 <- Plotter(data,"test2")
When I call ggarrange(plot1,plot2,ncol=2),
I get an error stating that ggarrange only accepts a ggplot,glist, but you have a list??
I checked the class of plot1 and it’s a list?
I also tried converting it to a grob, but that does not work. Can someone please let me know what I’m missing??
This works for me
library(gridExtra)
Plotter<- function (df,title){
Plots <-ggplot(df,aes(x=mpg,y=wt))+geom_col()+ggtitle(title)
print(Plots)
return(Plots)
}
data = data.frame(mpg = 1:10, wt = 1:10)
plot1 <- Plotter(data,"test")
plot2 <- Plotter(data,"test2")
grid.arrange(plot1,plot2,ncol=2)
i am struggling with R's print behavior when sourcing a function.
I want to write a function that generates a ggplot and returns that plot in order
to save it later.
So it should look something like this
file: func.R
make_plot <- function(data){
p <- ggplot(...)
print(p)
return(p)
}
source('func.R')
p <- make_plot(data)
ggsave('somewhere.png', plot=p)
my problem is that the generated plots are empty, and calling print(p)
outside of the function generates a empty plot as well.
If I run the code inside of the function interactively, everything is fine.
Any ideas?
thank you for your quick responses. I am very sorry, but the fault was all mine, the real code looked something like
file: func.R
make_plot <- function(data){
p <- ggplot(...)
p + geom_vline ...
print(p)
return(p)
}
source('func.R')
p <- make_plot(data)
ggsave('somewhere.png', plot=p)
So the retuned graphic object really was just empty.
It should obviously have been
p <- ggplot(...)
p <- p + geom_vline ...
thank you very much!
I have a two-rows figure which is composed by a plot on the first row and by a list of plots on the second row.
With knitr I do
\documentclass{article}
\begin{document}
<<plots, fig.width='\\textwidth', echo = FALSE, fig.height=5, fig.width = 10, warning = FALSE>>=
require(ggplot2)
plot <- ggplot() + geom_point(data=cars, aes(speed, dist)) + labs(x=NULL, y=NULL)
# create plot with function
makePlot <- function(myLabel) {
ggplot() + geom_point(data=cars, aes(speed, dist)) + labs(x=NULL,y=NULL,title=myLabel)
}
list_plot <- lapply(c("one","two","three"), makePlot)
require(gridExtra)
grob <- do.call(grid.arrange, c(list_plot, nrow=1, ncol=3)) # here R sends the plots to the graphical device!
grid.arrange(plot,
grob,
nrow=3)
#
\end{document}
which produces
The issue is that I do.call my list of plots, which immediately send to the graphical devices the plots.
Is there a fix to this, either in knitr or by avoiding do.call to spit the plot when passing it to grob?
Using ??grid.arrange we find the help page for arrangeGrob. This specifies the following:
arrangeGrob: return a grob without drawing
grid.arrange: draw on the current device
marrangeGrob: interface to arrangeGrob that can dispatch on multiple pages
The solution is therefore to use arrangeGrob instead of grid.arrange.
An added benefit is that a list of grobs can be passed with the grobs argument, so we can do away with the do.call construct.
I have a list, p, where each element of p is a list of ggplot2 plotting objects.
I would like to output a single pdf containing all the plots in p such that the plots in p[[1]] are on page 1, the plots in p[[2]] are on page 2, etc. How might I do this?
Here's some example code to provide you with the data structure I'm working with--apologies for the boring plots, I picked variables at random.
require(ggplot2)
p <- list()
cuts <- unique(diamonds$cut)
for(i in 1:length(cuts)){
p[[i]] <- list()
dat <- subset(diamonds, cut==cuts[i])
p[[i]][[1]] <- ggplot(dat, aes(price,table)) + geom_point() +
opts(title=cuts[i])
p[[i]][[2]] <- ggplot(dat, aes(price,depth)) + geom_point() +
opts(title=cuts[i])
}
This solution is independent of whether the lengths of the lists in the list p are different.
library(gridExtra)
pdf("plots.pdf", onefile = TRUE)
for (i in seq(length(p))) {
do.call("grid.arrange", p[[i]])
}
dev.off()
Because of onefile = TRUE the function pdf saves all graphics appearing sequentially in the same file (one page for one graphic).
Here is the most elegant solution to exporting a list of ggplot objects into a single pdf file using ggplot2::ggsave() and gridExtra::marrangeGrob().
library(ggplot2)
library(gridExtra)
Let's say you create multiple plots using lapply()
p <- lapply(names(mtcars), function(x) {
ggplot(mtcars, aes_string(x)) +
geom_histogram()
})
Save list of p plots:
ggsave(
filename = "plots.pdf",
plot = marrangeGrob(p, nrow=1, ncol=1),
width = 15, height = 9
)
Here is a simpler version of Sven's solution for the R beginners who would otherwise blindly use the do.call and nested lists that they neither need nor understand. I have empirical evidence. :)
library(ggplot2)
library(gridExtra)
pdf("plots.pdf", onefile = TRUE)
cuts <- unique(diamonds$cut)
for(i in 1:length(cuts)){
dat <- subset(diamonds, cut==cuts[i])
top.plot <- ggplot(dat, aes(price,table)) + geom_point() +
opts(title=cuts[i])
bottom.plot <- ggplot(dat, aes(price,depth)) + geom_point() +
opts(title=cuts[i])
grid.arrange(top.plot, bottom.plot)
}
dev.off()
I've tried some of these solutions but with no success. I researched a little more and found a solution that worked perfectly for me. It saves all my graphics in a single pdf file, each chart on one page.
library(ggplot2)
pdf("allplots.pdf",onefile = TRUE)
for(i in glist){
tplot <- ggplot(df, aes(x = as.factor(class), y = value))
print(tplot)
}
dev.off()
Here's one solution, but I don't particularly like it:
ggsave("test.pdf", do.call("marrangeGrob", c(unlist(p,recursive=FALSE),nrow=2,ncol=1)))
The problem is that it relies on there being the same number of plots in each group. If all(sapply(p, length) == 2) were false, then it would break.
A solution that worked for me with ggpubr package (package on Github, code for installation: devtools::install_github("kassambara/ggpubr")).
Let's say you have 4 plots p1, p2, p3 and p4.
library(ggpubr)
multi.page <- ggarrange(p1,p2,p3,p4, nrow=1, ncol=1) # for one plot per page
multi.page[[1]] # for seeing the first plot
ggexport(multi.page, filename="my-plots.pdf")
More examples of ggpubr use: http://www.sthda.com/english/articles/24-ggpubr-publication-ready-plots/81-ggplot2-easy-way-to-mix-multiple-graphs-on-the-same-page/
Here's a function based on Sven's approach, including the roxygen2 documentation and an example.
#' Save list of ggplot2 objects to single pdf
#'
#' #param list (list) List of ggplot2 objects.
#' #param filename (chr) What to call the pdf.
#'
#' #return Invisible NULL.
#' #export
#'
#' #examples
#' #plot histogram of each numeric variable in iris
#' list_iris = map(names(iris[-5]), ~ggplot(iris, aes_string(.)) + geom_histogram())
#' #save to a single pdf
#' GG_save_pdf(list_iris, "test.pdf")
GG_save_pdf = function(list, filename) {
#start pdf
pdf(filename)
#loop
for (p in list) {
print(p)
}
#end pdf
dev.off()
invisible(NULL)
}
A nice solution without the gridExtra package:
library(plyr)
library(ggplot2)
li = structure(p, class = c("gglist", "ggplot"))
print.gglist = function(x, ...) l_ply(x, print, ...)
ggsave(li, file = "test.pdf")