Test saving ggplots - r

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

Related

How to plot a high resolution image in ggplot without saving the file?

I am working on a restricted data online which I do not have access to. I can still get the outputs of my codes, but I cannot access any saved files of the outputs. Options like ggsave and other ones permit to have high-resolution quality figures, but then I cannot use these as I do not have access to the folders. Is there another way to produce these figures without having to save them? I can have access to the figures that plot in the R interface.
Let's suppose we use this code:
plot= ggplot(mtcars, aes(displ, hwy)) +
geom_point() +
geom_smooth()
If we type plot, R will display the graph. Is there a way to modify its resolution, length and width? and then to type plot and get these modifications?
For running remote scripts (e.g. via ssh) that output a svg representation of a plot, you might consider using repr.
Here's an example code (hack) that can work in non interactive mode on linux servers:
sink(file = "/dev/null") # suppresses all the output
library(ggplot2)
library(repr)
png(filename = "/dev/null") # start an arbitrary device. (it does not really matter)
dev.control("enable") #set this to allow recording from the device
g <- ggplot(mtcars, aes(disp, hp)) +
geom_point() +
geom_smooth()
print(g)
p <- recordPlot() #record it
dev.off() #close the device
sink() #restore the output
cat(repr_svg(p), width = 7, height = 7)) #print out the svg
Save the script to e.g. example_script.R, then run in a terminal
cat example_script.R | ssh myuser#myhost Rscript '-' > example.svg
This will save the results in the file example.svg

Why does ggplotly does not work in rmarkdown the same way ggplot does

I would like to use ggplotly for it's side effect the same way ggplot does or even graphics does. By this I mean when I knitr::knit or rmarkdown::render a Rmd document I expect print(obj) where obj is a ggplotly objcet to be in the report and that's not the case.
Can anyone explain what is going on?
Can anyone tell me how I can achieve want I want to do. I want to be able to plot a ggplotly graph into a function without returning the object (I want to return the underlying data of the graph) and I'd like the code to work for both ggplot and ggplotly (i.e. use the same code for a ggplot or ggplotly)
question.R file
#+ libs, echo = FALSE
suppressMessages({
library(ggplot2)
library(plotly)
library(rmarkdown)
})
#+ functions decl, echo = FALSE
df <- data.frame(x = 1:5, y = 1:5)
f_0 <- function(df) {
p <- ggplot(df, aes(x, y)) + geom_line()
# p or plot(p) or print(p) works
print(p)
return(df)
}
f_1 <- function(df) {
p <- ggplot(df, aes(x, y)) + geom_line()
p <- ggplotly(p)
# plot(p) crashes
# print p does not print in report
print(p)
# p standalone does not work either
p
return(df)
}
#' # plots
#' plot 0
#+ plot_0
res_0 <- f_0(df)
#' plot 1
#+ plot_1
res_1 <- f_1(df)
Render this file
rmarkdown::render("question.R")
The output
Editorial comment: As a point of style, it's usually a good idea to separate computation and plotting into distinct functions, because it increases modularity, makes code easier to maintain, and allows for finer control without parameter creep. The output of individual functions can then be easily mapped to separate knitr chunks.
Best solution: I know that you are specifically asking about not returning the plot object, but I just want to point out that returning it alongside the results provides the cleanest and most elegant solution:
---
output: html_document
---
```{r include=FALSE}
library( tidyverse )
df <- data_frame( x=1:5, y=1:5 )
```
```{r}
f <- function(df) {
gg <- ggplot(df, aes(x,y)) + geom_point()
list( result=df, plot=plotly::ggplotly(gg) )
}
res <- f(df)
res$plot
```
However, if you absolutely cannot return a plotly object from a function, you have some alternatives.
Option 1: Store the plotly object to the parent frame, providing access to it from the knitr chunk.
```{r}
f1 <- function(df) {
gg <- ggplot(df, aes(x,y)) + geom_point()
assign("ggp", plotly::ggplotly(gg), envir=parent.frame())
df # NOT returning a plot
}
res1 <- f1(df)
ggp # Let knitr handle the rendering naturally
```
Option 2: Render the plot to a temporary .html and then import it as an iframe
```{r, results='asis'} # <-- note the "asis" chunk option
f2 <- function(df)
{
gg <- ggplot(df, aes(x,y)) + geom_point()
htmlwidgets::saveWidget( plotly::ggplotly(gg), "temp.html")
print( htmltools::tags$iframe(src="temp.html", width=640, height=480) )
df # NOT returning a plot
}
res2 <- f2(df)
```
Explanation: Yihui can correct me if I'm wrong, but knitr basically does "Option 2" behind the scenes. It renders htmlwidgets, such as plotly objects, into temporary .html files and then combines those temporaries to produce the final document. The reason it fails inside a function is because the temporary file gets deleted when execution leaves the function scope. (You can replicate this yourself by using tempfile() instead of the persistent "temp.html"; the iframe object will display a "file not found" error in the final document.) There's probably a way to modify knitr hooks to retain the temporary files, but it's beyond my knowledge. Lastly, the reason you don't have this issue with basic ggplot objects is because their output goes to the plotting device, which persists across calling frames.

Save and Load a ggplot plot

I am working on a large shinydashboard and was keeping my code for modeling in a separate file from the main app.R. The problem is that I need to plot my data. This requires that I save my ggplots from one file and load them in my main app.R file. How can I save my ggplots and load them.
As a simple example lets say I have the following
#make plot
> p <- mtcars %>%
ggplot(aes(x = mpg, y = cyl))+geom_point()
#save plot
> save(file=here::here("plots/a_plot.Rdata"),p)
#load plot
> p <- load(file=here::here("plots/trans_arima.Rdata"))
> p
[1] "p"
How can I load my ggplot?
You can save your plot as a png file and then load it back into youyr file
you have several option for saving your plot. you could use ggplot2s' ggsave()
function or you could use the save_plot() from the cowplot package. save_plot() is said to
give you more flexibility when it comes file adjusting hence my pick.
You can explore both.
refer to https://rdrr.io/cran/cowplot/man/save_plot.html to read more about save_plot.
tmp = data.frame(first = c('a','b','c','d','e','f','g','h','i','j','k','l','m','n'),
second = c(2,3,4,5,2,3,4,5,6,3,4,4,6, 7))
plot_tmp = ggplot(tmp, aes(first, second)) + geom_bar(stat = 'identity')
dev.new()
if("png" %in% installed.packages()){
library(png)
}else{
install.packages("png")
library(png)
}
save_plot("~/plot_tmp.png", plot_tmp, base_height = NULL, base_aspect_ratio = 1.618,
base_width = 6)
Use the following steps to load files into your shiny using by using the
#read plot
library(OpenImageR)
img<-OpenImageR::readImage("~/plot_tmp.png")
imageShow(img)
Hopefully this helps. To read more about OpenImageR and how you can use it in shiny please go to https://cran.r-project.org/web/packages/OpenImageR/vignettes/The_OpenImageR_package.html
have fun!!!

tikz produces empty file in R loop

I am using the tikzDevice library in R to produce tikzpicture plots in latex. It works when plots are produced one by one. However, when I want to produce the plots in a loop, I get empty files. This issue is illustrated in the following minimal example:
library(ggplot2)
library(tikzDevice)
df <- data.frame(
x = c(1, 2),
y = c(1, 2)
)
path <- 'some path here'
for (j in 1:25){
filename <- paste(path, j, sep = '')
filename <- paste(filename, '.tex', sep = '')
tikz(file = filename)
plot <-
ggplot(data=df, aes(x=x, y=y))
plot
dev.off()
}
This produces 25 empty plots. However, if I run the code without the for loop (for instance, copying the content in the loop 25 times and changing j accordingly, then it produces the plots. What is the problem here?
plot(plot) or one loses the plot

How to store r ggplot graph as html code snippet

I am creating an html document by creating various objects with ggplotly() and htmltools functions like h3() and html(). Then I submit them as a list to htmltools::save_html() to create an html file.
I would like to add ggplot charts directly as images, rather than attaching all the plotly bells and whistles. In the end, I will create a self-contained html file (no dependencies), and the plotly stuff would make that file excessively large.
Is there some function that converts a ggplot object into some html-type object? Or do I have to save the ggplot as a .png file, then read the .png file into some object that I add to the list in the save_html() function?
My R code looks something like this:
library("tidyverse")
library("plotly")
library("htmltools")
HTMLOut <- "c:/Users/MrMagoo/My.html")
df <- data.frame(x=1:25, y=c(1:25*1:25))
g7 <- ggplot(df,aes(x=x, y=y)) + geom_point()
p7 <- ggplotly(g7) # I would like to use something other than ggplotly here. Just capturing the ggplot as an image would be fine.
# create other objects to add to the html file
t7 <- h2(id="graph7", "Title for graph #7")
d7 <- p("description of graph 7")
save_html(list(t7, p7, d7), HTMLOut)
# of course, the real code has many more objects in that list – more graphs, text, tables, etc.
I would like to replace the plotly object (p7) with something that just presents g7 in a way that would not cause an error in the save_html function.
I had hoped to find a function that could directly Base64 encode a ggplot object, but it seems that I first need to output the 'ggplot' object as a .png file (or SVG, per Teng L, below), then base64-encode it. I was hoping there was a more direct way, but I may end up doing that, as in https://stackoverflow.com/a/33410766/3799203 , ending it with
g7img <- "<img src=\"data:image/png;base64,(base64encode string)\""
g7img <- htmltools::html(g7img)
If you want to save the plot as a dynamic plotly graph, you could use htmlwidgets::saveWidget. This will produce a stand-alone html file.
Here is a minimal example:
library(tidyverse);
library(plotly);
library(htmlwidgets);
df <- data.frame(x = 1:25, y = c(1:25 * 1:25))
gg <- ggplot(df,aes(x = x, y = y)) + geom_point()
# Save ggplotly as widget in file test.html
saveWidget(ggplotly(gg), file = "test.html");
I ended up generating a temparory image file, then base64 encoding it, within a function I called encodeGraphic() (borrowing code from LukeA's post):
library(ggplot2)
library(RCurl)
library(htmltools)
encodeGraphic <- function(g) {
png(tf1 <- tempfile(fileext = ".png")) # Get an unused filename in the session's temporary directory, and open that file for .png structured output.
print(g) # Output a graphic to the file
dev.off() # Close the file.
txt <- RCurl::base64Encode(readBin(tf1, "raw", file.info(tf1)[1, "size"]), "txt") # Convert the graphic image to a base 64 encoded string.
myImage <- htmltools::HTML(sprintf('<img src="data:image/png;base64,%s">', txt)) # Save the image as a markdown-friendly html object.
return(myImage)
}
HTMLOut <- "~/TEST.html" # Say where to save the html file.
g <- ggplot(mtcars, aes(x=gear,y=mpg,group=factor(am),color=factor(am))) + geom_line() # Create some ggplot graph object
hg <- encodeGraphic(g) # run the function that base64 encodes the graph
forHTML <- list(h1("My header"), p("Lead-in text about the graph"), hg)
save_html(forHTML, HTMLOut) # output it to the html file.
I think what you want may be close to one of the following:
Seems you are creating an HTML report but hasn't checked out RMarkdown. It comes with Base64 encode. When you create an RMarkdown report, pandoc automatically converts any plots into an HTML element within the document, so the report is self-contained.
SVG plots. This is less likely to be what you might want, but SVG plots are markup-language based and may be easily portable. Specify .svg extension when you use ggsave() and you should be getting an SVG image. Note that SVG is an as-is implementation of the plot, so if can be huge in file size if you have thousands of shapes and lines.
This is an extension to the Maurits Evers post. In this answer I'm showing how to combine multiple plotly plots in the same html file in an organized fashion:
library("plotly")
library("htmltools")
# a small helper function to avoid repetition
my_func <- function(..., title){
## Description:
## A function to add title to put multiple gg plotly objects under a html heading
##
## Arguments:
## ...: a list of gg objects
## title: a character vector to specify the heading text
# get the ... in list format
lst <- list(...)
# create the heading
tmp_title <- htmltools::h1(title)
# convert each ggplot to ggplotly and put them under the same div html tag
tmp_plot <- lapply(lst, ggplotly) |>
htmltools::div()
# return the final object as list
return(list(tmp_title, tmp_plot))
}
# a toy data
df <- data.frame(x = 1:25, y = c(1:25 * 1:25))
# the ggplot object using the toy data
gg <- ggplot(df,aes(x = x, y = y)) + geom_point()
# put everything in order
final_list <- list(my_func(obj = list(gg, gg, gg), title = "The first heading"),
my_func(obj = list(gg, gg), title = "The second heading"))
# write to disk as a unified HTML file
htmltools::save_html(html = final_list,
file = "index.html"))
Disclaimer: I specifically did this to avoid using widgetframe R package and to be completely on par with the documentation of plotly-r. You can read the link if you are comfortable with adding extra dependency and extra abstraction layer. I prefer to use packages if and only if necessary. :)

Resources