Сохраняйте графики, сделанные в блестящем приложении

85

Я пытаюсь понять, как использовать downloadButton для сохранения сюжета с помощью shiny. Пример в пакете демонстрирует downloadButton / downloadHandler для сохранения .csv. Я собираюсь сделать воспроизводимый пример на основе этого.

За 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')
  )
))

За 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())
    }
  )
})

Если вы отвечаете на этот вопрос, вы, вероятно, знакомы с этим, но чтобы это работало, сохраните приведенное выше в отдельных сценариях ( ui.Rи server.Rв папке ( foo) в рабочем каталоге. Чтобы запустить блестящее приложение, запустите runApp("foo").

При использовании ggsaveя получаю сообщение об ошибке, указывающее, что ggsave не может использовать эту filenameфункцию (я думаю). Если я использую стандартное графическое устройство (как показано ниже), оно Download Plotработает без ошибок, но не записывает графику.

Будем признательны за любые советы по работе с downloadHandler для написания графиков.

Alexwhan
источник

Ответы:

69

Не уверен, что этот вопрос все еще активен, но это первый вопрос, который возник при поиске «сохранение графиков в блестящем приложении», поэтому я хотел быстро добавить, как заставить ggsave работать с downloadHandler в соответствии со строками исходного вопроса.

Альтернативные стратегии, предложенные juba с использованием прямого вывода вместо ggsave, и альтернативная стратегия, предложенная самим alexwhan, отлично работают, это как раз для тех, кто абсолютно хочет использовать ggsave в downloadHandler).

Проблема, о которой сообщил alexwhan, вызвана тем, что ggsave пытается сопоставить расширение файла с правильным графическим устройством. Однако временный файл не имеет расширения, поэтому сопоставление не удается. Это можно исправить, специально установив устройство в ggsaveвызове функции, как в исходном примере кода (для 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)
    }
)

Этот вызов в основном принимает deviceфункцию для того, pngчто ggsaveправопреемников внутренне (вы можете посмотреть на ggsaveфункцию коде , чтобы увидеть синтаксис jpg, pdfи т.д.). Возможно, в идеале можно было бы указать расширение файла (если оно отличается от имени файла - как в случае с временным файлом) в качестве ggsaveпараметра, но эта опция в настоящее время недоступна в ggsave.


Минимальный автономный рабочий пример:

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    

Обновить

Начиная с версии 2.0.0 ggplot2, ggsaveфункция поддерживает ввод символов для deviceпараметра, это означает, что временный файл, созданный с помощью downloadHandler, теперь может быть сохранен с помощью прямого вызова ggsave, указав, что используемое расширение должно быть, например "pdf"(вместо передачи в функции устройства). Это упрощает приведенный выше пример до следующего

output$downloadPlot <- downloadHandler(
    filename = function() { paste(input$dataset, '.png', sep='') },
    content = function(file) {
        ggsave(file, plot = plotInput(), device = "png")
    }
)
Sebkopf
источник
1
Я считаю, что ваш ответ здесь действительно правильный. Вы также можете просто использовать ggsave(file, plotInput(), device = png)вместо создания функции устройства (оболочки).
Yihui Xie
@sebkopf Я пропустил ваш ответ за год с небольшим!
alexwhan
1
@Yihui Это решение не работает для меня: R версии 3.1.0, ggplot2_1.0.0 shiny_0.10.1. Появится окно «Сохранить», нажмите «Сохранить», но файл не сохраняется. Кто-нибудь может подтвердить?
zx8754
3
@ zx8754 Я только что добавил к ответу полный пример. Обратите внимание, что вы должны запускать его в своем веб-браузере, а не просматривать в RStudio, так как программа просмотра RStudio имеет известную ошибку, заключающуюся в невозможности загрузки файлов.
Yihui Xie
1
@sebkopf Да, я понял это после того, как попробовал реальный пример, поэтому мой первый комментарий здесь был на самом деле неправильным. Спасибо за разъяснения!
Yihui Xie
24

Вот решение, которое позволяет использовать ggsave для сохранения блестящих участков. Он использует логический флажок и текстовый ввод для вызова ggsave(). Добавьте это в ui.Rфайл внутри sidebarPanel:

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

Затем добавьте это в server.Rфайл вместо текущей output$plotфункции reactivePlot:

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

Затем пользователь может ввести желаемое имя файла в текстовое поле (без расширения) и установить флажок, чтобы сохранить его в каталоге приложения. При снятии флажка график снова печатается. Я уверен, что есть более изящные способы сделать это, но, по крайней мере, теперь я могу использовать ggsave и cairo в Windows для гораздо более красивой графики в формате png.

Пожалуйста, добавьте любые предложения, которые у вас могут быть

Alexwhan
источник
Без isolateблока вокруг input$filename, любое изменение в filenameтекстовом поле также будет запрашивать сохранение файла, если этот флажок установлен.
jpd527 03
23

У меня не получилось заставить его работать ggsave, но со стандартным вызовом png()вроде нормально.

Я изменил только output$downloadPlotчасть вашего server.Rфайла:

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

Обратите внимание, что у меня были проблемы с версией shiny 0.3, но она работает с последней версией от Github:

library(devtools)
install_github("shiny","rstudio")
Джуба
источник
Хорошо, я собираюсь согласиться с тем, что ggsave не будет работать на данном этапе разбирательства с downloadHandler. shiny 0.3 разваливается с downloadHandler, вы правы. Я опубликую альтернативное решение, которое я выяснил, избегая downloadHandler, которое позволит ggsave работать.
alexwhan
1
@juba есть идеи, почему эта попытка вывода в PDF с помощью аналогичного метода (не ggplot2) не работает? У меня просто сломанный PDF-файл, который не открывается. Может ли plotInput не выдавать сюжет вместо сюжетного объекта?
geotheory 03
20

Это старый, но по-прежнему самый популярный вариант, когда кто-то набирает в Google «R shiny save ggplot», поэтому я предложу другой обходной путь. Очень просто ... вызовите ggsave в той же функции, которая отображает ваш график, которая сохранит график как файл на сервере.

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

Затем используйте downloadHandler и используйте file.copy()для записи данных из существующего файла в параметр «файл».

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

Работает на меня.

вокалоидикт
источник