Save plots made in a shiny app
Not sure if this question is still active but it's the first one that came up when searching for "saving plots in shiny app" so I wanted to quickly add how to get ggsave to work with downloadHandler along the lines of the original question.
The alternative strategies suggested by juba using direct output instead of ggsave and alternative strategy suggested by alexwhan himself both work great, this is just for those who absolutely want to use ggsave in the downloadHandler).
The problem reported by alexwhan is caused by ggsave trying to match the file extension to the correct graphics device. The temporary file, however, doesn't have an extension so the matching fails. This can be remedied by specifically setting the device in the ggsave
function call, like so in the original code example (for a png):
output$downloadPlot <- downloadHandler(
filename = function() { paste(input$dataset, '.png', sep='') },
content = function(file) {
device <- function(..., width, height) grDevices::png(..., width = width, height = height, res = 300, units = "in")
ggsave(file, plot = plotInput(), device = device)
}
)
This call basically takes the device
function for a png
that ggsave
assigns internally (you can look at the ggsave
function code to see the syntax for jpg
, pdf
, etc). Perhaps, ideally, one could specify the file extension (if different from the file name - as is the case here for the temporary file) as a ggsave
parameter but this option is currently not available in ggsave
.
A minimal self-contained working example:
library(shiny)
library(ggplot2)
runApp(list(
ui = fluidPage(downloadButton('foo')),
server = function(input, output) {
plotInput = function() {
qplot(speed, dist, data = cars)
}
output$foo = downloadHandler(
filename = 'test.png',
content = function(file) {
device <- function(..., width, height) {
grDevices::png(..., width = width, height = height,
res = 300, units = "in")
}
ggsave(file, plot = plotInput(), device = device)
})
}
))
sessionInfo()
# R version 3.1.1 (2014-07-10)
# Platform: x86_64-pc-linux-gnu (64-bit)
#
# locale:
# [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
# [3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
# [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
# [7] LC_PAPER=en_US.UTF-8 LC_NAME=C
# [9] LC_ADDRESS=C LC_TELEPHONE=C
# [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
#
# attached base packages:
# [1] stats graphics grDevices utils datasets methods base
#
# other attached packages:
# [1] ggplot2_1.0.0 shiny_0.10.1
#
# loaded via a namespace (and not attached):
# [1] bitops_1.0-6 caTools_1.17 colorspace_1.2-4 digest_0.6.4
# [5] formatR_1.0 grid_3.1.1 gtable_0.1.2 htmltools_0.2.6
# [9] httpuv_1.3.0 labeling_0.2 MASS_7.3-34 munsell_0.4.2
# [13] plyr_1.8.1 proto_0.3-10 Rcpp_0.11.2 reshape2_1.4
# [17] RJSONIO_1.3-0 scales_0.2.4 stringr_0.6.2 tools_3.1.1
# [21] xtable_1.7-3
Update
As of ggplot2 version 2.0.0, the ggsave
function supports character input for the device
parameter, that means the temporary file created by the downloadHandler can now be saved with a direct call to ggsave
by specifying that the extension to be used should be e.g. "pdf"
(rather than passing in a device function). This simplifies the above example to the following
output$downloadPlot <- downloadHandler(
filename = function() { paste(input$dataset, '.png', sep='') },
content = function(file) {
ggsave(file, plot = plotInput(), device = "png")
}
)
Here's a solution that allows using ggsave for saving shiny plots. It uses a logical checkbox and text input to call ggsave()
. Add this to the ui.R
file inside sidebarPanel
:
textInput('filename', "Filename"),
checkboxInput('savePlot', "Check to save")
Then add this to the server.R
file instead of the current output$plot
reactivePlot function:
output$plot <- reactivePlot(function() {
name <- paste0(input$filename, ".png")
if(input$savePlot) {
ggsave(name, plotInput(), type="cairo-png")
}
else print(plotInput())
})
A user can then type the desired filename in the textbox (without extension) and tick the checkbox to save in the app directory. Unchecking the box prints the plot again. I'm sure there are neater ways of doing this, but at least I can now use ggsave and cairo in windows for much nicer png graphics.
Please add any suggestions you may have.
I didn't manage to make it work with ggsave
, but with a standard call to png()
it seems to be okay.
I only changed the output$downloadPlot
part of your server.R
file :
output$downloadPlot <- downloadHandler(
filename = function() { paste(input$dataset, '.png', sep='') },
content = function(file) {
png(file)
print(plotInput())
dev.off()
})
Note that I had some problems with the 0.3 version of shiny, but it works with the latest from Github :
library(devtools)
install_github("shiny","rstudio")