Salve plotagens feitas em um aplicativo brilhante

85

Estou tentando descobrir como usar o downloadButton para salvar um gráfico com brilho. O exemplo no pacote demonstra downloadButton / downloadHandler para salvar um .csv. Vou fazer um exemplo reproduzível com base nisso.

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

Para 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 você está respondendo a esta pergunta, provavelmente está familiarizado com isso, mas para fazer isso funcionar, salve o acima em scripts separados ( ui.Re server.Rem uma pasta ( foo) dentro do diretório de trabalho. Para executar o aplicativo brilhante, execute runApp("foo").

Usando ggsave, recebo uma mensagem de erro indicando que ggsave não pode usar a filenamefunção (eu acho). Se eu usar o dispositivo gráfico padrão (como abaixo), Download Plotfunciona sem erro, mas não grava o gráfico.

Quaisquer dicas para fazer o downloadHandler trabalhar para escrever plotagens serão apreciadas.

alexwhan
fonte

Respostas:

69

Não tenho certeza se esta questão ainda está ativa, mas é a primeira que surgiu ao pesquisar por "salvar plotagens em aplicativo brilhante", então eu queria adicionar rapidamente como fazer o ggsave funcionar com downloadHandler seguindo as linhas da questão original.

As estratégias alternativas sugeridas por juba usando saída direta em vez de ggsave e a estratégia alternativa sugerida pelo próprio alexwhan funcionam muito bem, isso é apenas para aqueles que desejam absolutamente usar ggsave no downloadHandler).

O problema relatado por alexwhan é causado por ggsave tentando corresponder a extensão do arquivo ao dispositivo gráfico correto. O arquivo temporário, entretanto, não tem uma extensão, então a correspondência falha. Isso pode ser remediado definindo especificamente o dispositivo na ggsavechamada de função, como no exemplo de código original (para um 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 chamada basicamente assume a devicefunção para a pngque ggsaveatribui internamente (você pode olhar para o ggsavecódigo de função para ver a sintaxe para jpg, pdf, etc). Talvez, idealmente, seja possível especificar a extensão do arquivo (se diferente do nome do arquivo - como é o caso aqui para o arquivo temporário) como um ggsaveparâmetro, mas esta opção não está disponível atualmente em ggsave.


Um exemplo de trabalho independente 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    

Atualizar

A partir do ggplot2 versão 2.0.0, a ggsavefunção suporta entrada de caracteres para o deviceparâmetro, o que significa que o arquivo temporário criado pelo downloadHandler pode agora ser salvo com uma chamada direta para ggsaveespecificando que a extensão a ser usada deve ser por exemplo "pdf"(em vez de passar em uma função de dispositivo). Isso simplifica o exemplo acima para o seguinte

output$downloadPlot <- downloadHandler(
    filename = function() { paste(input$dataset, '.png', sep='') },
    content = function(file) {
        ggsave(file, plot = plotInput(), device = "png")
    }
)
sebkopf
fonte
1
Eu acredito que sua resposta é realmente a correta aqui. Você também pode simplesmente usar em ggsave(file, plotInput(), device = png)vez de criar uma função de dispositivo (invólucro).
Yihui Xie
@sebkopf Eu perdi sua resposta no ano que se passou e um pouco!
alexwhan
1
@Yihui Esta solução não funciona para mim: R versão 3.1.0, ggplot2_1.0.0 brilhante_0.10.1. A caixa Salvar aparece, clique em salvar, mas nenhum arquivo é salvo. Alguém pode confirmar?
zx8754
3
@ zx8754 Acabei de adicionar um exemplo completo à resposta. Observe que você deve executá-lo em seu navegador da web em vez de visualizá-lo no RStudio, uma vez que o visualizador do RStudio tem um bug conhecido de não conseguir baixar arquivos.
Yihui Xie
1
@sebkopf Sim, percebi depois de experimentar um exemplo real, então meu primeiro comentário aqui estava realmente errado. Obrigado pelo esclarecimento!
Yihui Xie
24

Aqui está uma solução que permite o uso de ggsave para salvar plotagens brilhantes. Ele usa uma caixa de seleção lógica e entrada de texto para chamar ggsave(). Adicione isso ao ui.Rarquivo dentro de sidebarPanel:

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

Em seguida, adicione isso ao server.Rarquivo em vez da output$plotfunção reactivePlot atual :

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

Um usuário pode então digitar o nome do arquivo desejado na caixa de texto (sem extensão) e marcar a caixa de seleção para salvar no diretório do aplicativo. Desmarcar a caixa imprime o gráfico novamente. Tenho certeza de que existem maneiras mais simples de fazer isso, mas pelo menos agora posso usar ggsave e cairo no Windows para obter gráficos PNG muito mais agradáveis.

Por favor, adicione qualquer sugestão que você possa ter.

alexwhan
fonte
Sem um isolatebloco ao redor do input$filename, qualquer alteração na filenamecaixa de texto também solicitará um arquivo salvo se a caixa estiver marcada.
jpd527
23

Não consegui fazer funcionar com o ggsave, mas com uma chamada padrão png()parece estar tudo bem.

Eu só mudei a output$downloadPlotparte do seu server.Rarquivo:

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

Observe que tive alguns problemas com a versão 0.3 do brilhante, mas funciona com a versão mais recente do Github:

library(devtools)
install_github("shiny","rstudio")
Juba
fonte
OK, vou aceitar que ggsave não vai funcionar nesta fase do processo com downloadHandler. O 0.3 brilhante se desfaz com o downloadHandler, você está certo. Vou postar uma solução alternativa que descobri evitando o downloadHandler, que permitirá que o ggsave funcione.
alexwhan
1
@juba alguma idéia de por que essa tentativa de saída para pdf com um método semelhante (não ggplot2) não funciona? Acabei de receber um pdf quebrado que não abre. O plotInput não pode fornecer um gráfico em vez de um objeto de plotagem?
geotheory
20

Isso é antigo, mas ainda é o principal hit quando alguém digita "R bright save ggplot", então irei contribuir com outra solução alternativa. Muito simples ... chame ggsave na mesma função que exibe seu gráfico, o que salvará o gráfico como um arquivo no servidor.

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

Em seguida, use downloadHandler e use file.copy()para gravar os dados do arquivo existente no parâmetro "arquivo".

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

Funciona para mim.

vocaloidict
fonte