Salva i grafici realizzati in un'app brillante


85

Sto cercando di capire come usare downloadButton per salvare una trama con shiny. L'esempio nel pacchetto mostra downloadButton / downloadHandler per salvare un file .csv. Farò un esempio riproducibile basato su questo.

Per 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')
  )
))

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

Se stai rispondendo a questa domanda, probabilmente hai familiarità con questo, ma per farlo funzionare, salva quanto sopra in script separati ( ui.Re server.Rin una cartella ( foo) all'interno della directory di lavoro. Per eseguire l'app brillante, esegui runApp("foo").

Utilizzando ggsave, ottengo un messaggio di errore che indica che ggsave non può utilizzare la filenamefunzione (credo). Se utilizzo il dispositivo grafico standard (come sotto), Download Plotfunziona senza errori, ma non scrive il grafico.

Eventuali suggerimenti per far funzionare downloadHandler per la scrittura di grafici sarebbero apprezzati.

Risposte:


69

Non sono sicuro che questa domanda sia ancora attiva, ma è la prima che viene visualizzata durante la ricerca di "salvataggio di grafici in un'app brillante", quindi volevo aggiungere rapidamente come far funzionare ggsave con downloadHandler sulla falsariga della domanda originale.

Le strategie alternative suggerite da juba utilizzando l'output diretto al posto di ggsave e la strategia alternativa suggerita dallo stesso alexwhan funzionano entrambe alla grande, questo è solo per coloro che vogliono assolutamente usare ggsave nel downloadHandler).

Il problema segnalato da alexwhan è causato dal tentativo di ggsave di abbinare l'estensione del file al dispositivo grafico corretto. Il file temporaneo, tuttavia, non ha un'estensione, quindi la corrispondenza non riesce. Questo può essere risolto impostando specificamente il dispositivo nella ggsavechiamata di funzione, come nell'esempio di codice originale (per 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)
    }
)

Questa chiamata ha fondamentalmente la devicefunzione per un pngche ggsaveassegna internamente (si può guardare il ggsavecodice funzione per vedere la sintassi per jpg, pdfecc). Forse, idealmente, si potrebbe specificare l'estensione del file (se diversa dal nome del file, come nel caso del file temporaneo) come ggsaveparametro, ma questa opzione non è attualmente disponibile in ggsave.


Un esempio di lavoro autonomo minimo:

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    

Aggiornare

A partire dalla versione 2.0.0 di ggplot2, la ggsavefunzione supporta l'immissione di caratteri per il deviceparametro, il che significa che il file temporaneo creato da downloadHandler può ora essere salvato con una chiamata diretta a ggsavespecificando che l'estensione da utilizzare deve essere ad esempio "pdf"(invece di passare in una funzione del dispositivo). Ciò semplifica l'esempio precedente al seguente

output$downloadPlot <- downloadHandler(
    filename = function() { paste(input$dataset, '.png', sep='') },
    content = function(file) {
        ggsave(file, plot = plotInput(), device = "png")
    }
)

1
Credo che la tua risposta sia effettivamente quella corretta qui. Puoi anche usare semplicemente ggsave(file, plotInput(), device = png)invece di creare una funzione dispositivo (wrapper).
Yihui Xie

@sebkopf Mi sono persa la tua risposta nell'anno successivo e un po '!
alexwhan

1
@Yihui Questa soluzione non funziona per me: R versione 3.1.0, ggplot2_1.0.0 shiny_0.10.1. Viene visualizzata la finestra di salvataggio, fare clic su Salva, ma nessun file viene salvato. Qualcuno può confermare?
zx8754

3
@ zx8754 Ho appena aggiunto un esempio completo alla risposta. Nota che dovresti eseguirlo nel tuo browser web invece di visualizzarlo in RStudio, poiché il visualizzatore RStudio ha un bug noto di non essere in grado di scaricare file.
Yihui Xie

1
@sebkopf Sì, l'ho capito dopo aver provato un esempio reale, quindi il mio primo commento qui era effettivamente sbagliato. Grazie per il chiarimento!
Yihui Xie

24

Ecco una soluzione che consente di utilizzare ggsave per salvare trame brillanti. Utilizza una casella di controllo logica e un input di testo per chiamare ggsave(). Aggiungilo al ui.Rfile all'interno sidebarPanel:

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

Quindi aggiungilo al server.Rfile invece della output$plotfunzione reattivaPlot corrente :

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

Un utente può quindi digitare il nome del file desiderato nella casella di testo (senza estensione) e selezionare la casella di controllo per salvare nella directory dell'app. Deselezionando la casella viene stampato nuovamente il grafico. Sono sicuro che ci sono modi più ordinati per farlo, ma almeno ora posso usare ggsave e cairo in Windows per una grafica png molto più bella.

Si prega di aggiungere eventuali suggerimenti.


Senza un isolateblocco attorno a input$filename, qualsiasi modifica alla filenamecasella di testo richiederà anche il salvataggio di un file se la casella è selezionata.
jpd527

23

Non sono riuscito a farlo funzionare ggsave, ma con una chiamata standard png()sembra andare bene.

Ho cambiato solo la output$downloadPlotparte del tuo server.Rfile:

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

Nota che ho avuto alcuni problemi con la versione 0.3 di shiny, ma funziona con l'ultima di Github:

library(devtools)
install_github("shiny","rstudio")

OK, accetterò che ggsave non funzionerà in questa fase del procedimento con downloadHandler. lucido 0.3 cade a pezzi con downloadHandler, hai ragione. Pubblicherò una soluzione alternativa che ho scoperto evitando downloadHandler che consentirà a ggsave di funzionare.
alexwhan

1
@juba qualche idea del perché questo tentativo di output in pdf con un metodo simile (non ggplot2) non funziona? Ho appena ricevuto un pdf rotto che non si apre. PlotInput non può fornire una trama invece di un oggetto di trama?
geoteoria

20

Questo è vecchio, ma è ancora il risultato migliore quando qualcuno cerca su Google "R shiny save ggplot", quindi contribuirò con un'altra soluzione alternativa. Molto semplice ... chiama ggsave nella stessa funzione che mostra il tuo grafico, che salverà il grafico come file sul server.

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

Quindi, utilizzare downloadHandler e utilizzare file.copy()per scrivere i dati dal file esistente nel parametro "file".

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

Per me va bene.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.