Guarde los gráficos realizados en una aplicación brillante

85

Estoy tratando de averiguar cómo usar downloadButton para guardar un gráfico con shiny. El ejemplo del paquete demuestra downloadButton / downloadHandler para guardar un .csv. Voy a hacer un ejemplo reproducible basado en eso.

por ui.R

shinyUI(pageWithSidebar(
  headerPanel('Downloading Data'),
  sidebarPanel(
selectInput("dataset", "Choose a dataset:", 
            choices = c("rock", "pressure", "cars")),
    downloadButton('downloadData', 'Download Data'),
    downloadButton('downloadPlot', 'Download Plot')
  ),
  mainPanel(
    plotOutput('plot')
  )
))

por server.R

library(ggplot2)
shinyServer(function(input, output) {
  datasetInput <- reactive({
    switch(input$dataset,
           "rock" = rock,
           "pressure" = pressure,
           "cars" = cars)
  })

  plotInput <- reactive({
    df <- datasetInput()
    p <-ggplot(df, aes_string(x=names(df)[1], y=names(df)[2])) +
      geom_point()
  })

  output$plot <- renderPlot({
    print(plotInput())
  })

  output$downloadData <- downloadHandler(
    filename = function() { paste(input$dataset, '.csv', sep='') },
    content = function(file) {
      write.csv(datatasetInput(), file)
    }
  )
  output$downloadPlot <- downloadHandler(
    filename = function() { paste(input$dataset, '.png', sep='') },
    content = function(file) {
      ggsave(file,plotInput())
    }
  )
})

Si está respondiendo a esta pregunta, probablemente esté familiarizado con esto, pero para que esto funcione, guarde lo anterior en scripts separados ( ui.Ry server.Ren una carpeta ( foo) dentro del directorio de trabajo. Para ejecutar la aplicación brillante, ejecute runApp("foo").

Al usar ggsave, aparece un mensaje de error que indica que ggsave no puede usar la filenamefunción (creo). Si utilizo el dispositivo gráfico estándar (como a continuación), Download Plotfunciona sin error, pero no escribe el gráfico.

Se agradecería cualquier consejo para que downloadHandler funcione para escribir gráficos.

alexwhan
fuente

Respuestas:

69

No estoy seguro de si esta pregunta todavía está activa, pero es la primera que surgió al buscar "guardar gráficos en una aplicación brillante", así que quería agregar rápidamente cómo hacer que ggsave funcione con downloadHandler siguiendo las líneas de la pregunta original.

Las estrategias alternativas sugeridas por juba usando salida directa en lugar de ggsave y la estrategia alternativa sugerida por el mismo alexwhan funcionan muy bien, esto es solo para aquellos que absolutamente quieren usar ggsave en downloadHandler).

El problema informado por alexwhan se debe a que ggsave intenta hacer coincidir la extensión del archivo con el dispositivo gráfico correcto. Sin embargo, el archivo temporal no tiene extensión, por lo que la coincidencia falla. Esto se puede remediar configurando específicamente el dispositivo en la ggsavellamada a la función, como en el ejemplo de código original (para un 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)
    }
)

Esta llamada toma básicamente la devicefunción de pngque los ggsavecesionarios internos (se puede ver en el ggsavecódigo de función para ver la sintaxis jpg, pdfetc). Quizás, idealmente, se podría especificar la extensión del archivo (si es diferente del nombre del archivo, como es el caso aquí para el archivo temporal) como ggsaveparámetro, pero esta opción no está disponible actualmente en ggsave.


Un ejemplo de trabajo autónomo mínimo:

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    

Actualizar

A partir de la versión 2.0.0 de ggplot2, la ggsavefunción admite la entrada de caracteres para el deviceparámetro, lo que significa que el archivo temporal creado por downloadHandler ahora se puede guardar con una llamada directa al ggsaveespecificando que la extensión que se utilizará debe ser, por ejemplo, "pdf"(en lugar de pasar en una función de dispositivo). Esto simplifica el ejemplo anterior al siguiente

output$downloadPlot <- downloadHandler(
    filename = function() { paste(input$dataset, '.png', sep='') },
    content = function(file) {
        ggsave(file, plot = plotInput(), device = "png")
    }
)
sebkopf
fuente
1
Creo que tu respuesta es realmente la correcta aquí. También puede usar en ggsave(file, plotInput(), device = png)lugar de crear una función de dispositivo (envoltorio).
Yihui Xie
@sebkopf ¡Me perdí tu respuesta en el año intermedio y un poco!
alexwhan
1
@Yihui Esta solución no me funciona: R versión 3.1.0, ggplot2_1.0.0 shiny_0.10.1. Aparece el cuadro Guardar, haga clic en Guardar, pero no se guarda ningún archivo. ¿Alguien puede confirmar?
zx8754
3
@ zx8754 Acabo de agregar un ejemplo completo a la respuesta. Tenga en cuenta que debe ejecutarlo en su navegador web en lugar de verlo en RStudio, ya que el visor RStudio tiene un error conocido de no poder descargar archivos.
Yihui Xie
1
@sebkopf Sí, me di cuenta después de probar un ejemplo real, así que mi primer comentario aquí fue realmente incorrecto. ¡Gracias por la aclaración!
Yihui Xie
24

Aquí hay una solución que permite usar ggsave para guardar gráficos brillantes. Utiliza una casilla de verificación lógica y una entrada de texto para llamar ggsave(). Agregue esto al ui.Rarchivo dentro sidebarPanel:

textInput('filename', "Filename"),
checkboxInput('savePlot', "Check to save")

Luego agregue esto al server.Rarchivo en lugar de la output$plotfunción reactivePlot actual :

output$plot <- reactivePlot(function() {
    name <- paste0(input$filename, ".png")
    if(input$savePlot) {
      ggsave(name, plotInput(), type="cairo-png")
    }
    else print(plotInput())
  })

Luego, un usuario puede escribir el nombre de archivo deseado en el cuadro de texto (sin extensión) y marcar la casilla de verificación para guardar en el directorio de la aplicación. Al desmarcar la casilla, se imprime el gráfico nuevamente. Estoy seguro de que hay formas más ordenadas de hacer esto, pero al menos ahora puedo usar ggsave y cairo en Windows para obtener gráficos png mucho más agradables.

Agregue cualquier sugerencia que pueda tener.

alexwhan
fuente
Sin un isolatebloque alrededor del input$filename, cualquier cambio en el filenamecuadro de texto también solicitará que se guarde el archivo si el cuadro está marcado.
jpd527
23

No logré que funcione ggsave, pero con una llamada estándar png()parece estar bien.

Solo cambié la output$downloadPlotparte de tu server.Rarchivo:

 output$downloadPlot <- downloadHandler(
    filename = function() { paste(input$dataset, '.png', sep='') },
    content = function(file) {
      png(file)
      print(plotInput())
      dev.off()
    })

Tenga en cuenta que tuve algunos problemas con la versión 0.3 de shiny, pero funciona con la última de Github:

library(devtools)
install_github("shiny","rstudio")
juba
fuente
OK, voy a aceptar que ggsave no funcionará en esta etapa del procedimiento con downloadHandler. shiny 0.3 se desmorona con downloadHandler, tienes razón. Publicaré una solución alternativa que descubrí para evitar downloadHandler que permitirá que ggsave funcione.
alexwhan
1
@juba ¿alguna idea de por qué este intento de salida a pdf con un método similar (no ggplot2) no funciona? Acabo de recibir un pdf roto que no se abre. ¿PlotInput no puede entregar una trama en lugar de un objeto de trama?
geoteoría
20

Esto es antiguo, pero sigue siendo el principal éxito cuando alguien busca en Google "R shiny save ggplot", así que contribuiré con otra solución. Muy simple ... llame a ggsave en la misma función que muestra su gráfico, que guardará el gráfico como un archivo en el servidor.

output$plot <- renderPlot({
    ggsave("plot.pdf", plotInput())
    plotInput()
})

Luego, use downloadHandler y use file.copy()para escribir datos desde el archivo existente en el parámetro "file".

output$dndPlot <- downloadHandler(
    filename = function() {
        "plot.pdf"
    },
    content = function(file) {
        file.copy("plot.pdf", file, overwrite=TRUE)
    }
)

Funciona para mi.

vocaloidict
fuente