Zapisuj działki wykonane w błyszczącej aplikacji

85

Próbuję dowiedzieć się, jak użyć downloadButton, aby zapisać działkę z błyszczącym. Przykład w pakiecie demonstruje metodę downloadButton / downloadHandler, aby zapisać plik .csv. Na tej podstawie stworzę powtarzalny przykład.

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

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

Jeśli odpowiadasz na to pytanie, prawdopodobnie znasz to, ale aby to zadziałało, zapisz powyższe w osobnych skryptach ( ui.Ri server.Rw folderze ( foo) w katalogu roboczym. Aby uruchomić błyszczącą aplikację, uruchom runApp("foo").

Używając ggsave, otrzymuję komunikat o błędzie wskazujący, że ggsave nie może użyć filenamefunkcji (tak mi się wydaje). Jeśli korzystam ze standardowego urządzenia graficznego (jak poniżej), to Download Plotdziała bezbłędnie, ale nie pisze grafiki.

Wszelkie wskazówki dotyczące tego, aby downloadHandler pracował nad pisaniem fabuł, byłyby mile widziane.

alexwhan
źródło

Odpowiedzi:

69

Nie jestem pewien, czy to pytanie jest nadal aktywne, ale jest to pierwsze pytanie, które pojawiło się podczas wyszukiwania „zapisywania działek w błyszczącej aplikacji”, więc chciałem szybko dodać, jak sprawić, by ggsave działał z downloadHandler, zgodnie z oryginalnym pytaniem.

Alternatywne strategie sugerowane przez juba przy użyciu bezpośredniego wyjścia zamiast ggsave i alternatywna strategia sugerowana przez samego alexwhana działają świetnie, jest to tylko dla tych, którzy absolutnie chcą używać ggsave w downloadHandler).

Problem zgłoszony przez alexwhan jest spowodowany próbą dopasowania rozszerzenia pliku do odpowiedniego urządzenia graficznego przez ggsave. Jednak plik tymczasowy nie ma rozszerzenia, więc dopasowanie nie powiedzie się. Można temu zaradzić, ustawiając urządzenie w ggsavewywołaniu funkcji, tak jak w przykładzie oryginalnego kodu (dla pliku 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)
    }
)

Wezwanie to w zasadzie wykonuje devicefunkcję dla pngże ggsavecesjonariuszy wewnętrznie (można spojrzeć na ggsavekod funkcji, aby zobaczyć składnię jpg, pdfitp). Być może najlepiej byłoby określić rozszerzenie pliku (jeśli różni się od nazwy pliku - jak ma to miejsce w przypadku pliku tymczasowego) jako ggsaveparametr, ale ta opcja nie jest obecnie dostępna w ggsave.


Minimalny samodzielny przykład roboczy:

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    

Aktualizacja

Począwszy od wersji 2.0.0 ggplot2, ggsavefunkcja obsługuje wprowadzanie znaków dla deviceparametru, co oznacza, że ​​plik tymczasowy utworzony przez downloadHandler może być teraz zapisany z bezpośrednim wywołaniem do ggsavepoprzez określenie, że rozszerzenie, które ma być używane, powinno być np. "pdf"(Zamiast przekazywania w funkcji urządzenia). Upraszcza to powyższy przykład do następującego

output$downloadPlot <- downloadHandler(
    filename = function() { paste(input$dataset, '.png', sep='') },
    content = function(file) {
        ggsave(file, plot = plotInput(), device = "png")
    }
)
sebkopf
źródło
1
Uważam, że twoja odpowiedź jest tutaj poprawna. Możesz także po prostu użyć ggsave(file, plotInput(), device = png)zamiast tworzenia funkcji urządzenia (opakowania).
Yihui Xie
@sebkopf Brakowało mi twojej odpowiedzi w międzyczasie i trochę!
alexwhan
1
@Yihui To rozwiązanie nie działa dla mnie: R wersja 3.1.0, ggplot2_1.0.0 shiny_0.10.1. Pojawi się okno Zapisz, kliknij Zapisz, ale żaden plik nie zostanie zapisany. Czy ktoś może potwierdzić?
zx8754
3
@ zx8754 Właśnie dodałem pełny przykład do odpowiedzi. Zauważ, że powinieneś uruchomić go w przeglądarce internetowej zamiast przeglądać go w RStudio, ponieważ przeglądarka RStudio ma znany błąd polegający na niemożności pobierania plików.
Yihui Xie
1
@sebkopf Tak, zdałem sobie z tego sprawę po wypróbowaniu prawdziwego przykładu, więc mój pierwszy komentarz tutaj był właściwie błędny. Dziękuję za wyjaśnienie!
Yihui Xie
24

Oto rozwiązanie, które pozwala używać ggsave do zapisywania błyszczących działek. Do wywołania używa logicznego pola wyboru i wprowadzania tekstu ggsave(). Dodaj to do ui.Rpliku w środku sidebarPanel:

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

Następnie dodaj to do server.Rpliku zamiast bieżącej output$plotfunkcji reactivePlot:

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

Użytkownik może następnie wpisać żądaną nazwę pliku w polu tekstowym (bez rozszerzenia) i zaznaczyć pole wyboru, aby zapisać w katalogu aplikacji. Odznaczenie pola powoduje ponowne wydrukowanie wykresu. Jestem pewien, że są lepsze sposoby na zrobienie tego, ale przynajmniej mogę teraz używać ggsave i cairo w oknach, aby uzyskać znacznie ładniejszą grafikę png.

Dodaj wszelkie sugestie, które możesz mieć.

alexwhan
źródło
Bez isolatebloku wokół input$filename, każda zmiana w filenamepolu tekstowym będzie również powodować monit o zapisanie pliku, jeśli pole jest zaznaczone.
jpd527
23

Nie udało mi się z tym zrobić ggsave, ale przy standardowym wywołaniu png()wydaje się być w porządku.

Zmieniłem tylko output$downloadPlotczęść twojego server.Rpliku:

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

Zauważ, że miałem trochę problemów z wersją Shiny w 0.3, ale działa z najnowszą wersją Githuba:

library(devtools)
install_github("shiny","rstudio")
juba
źródło
OK, zaakceptuję, że ggsave nie będzie działać na tym etapie postępowania z downloadHandler. Shiny 0.3 rozpada się z downloadHandler, masz rację. Opublikuję alternatywne rozwiązanie, które wymyśliłem, unikając downloadHandler, które pozwoli ggsave działać.
alexwhan
1
@juba masz jakiś pomysł, dlaczego ta próba wyprowadzenia do pliku PDF za pomocą podobnej metody (innej niż ggplot2) nie działa? Po prostu otrzymuję uszkodzony plik PDF, który się nie otwiera. Czy plotInput nie może dostarczyć wykresu zamiast obiektu wydruku?
geoteoria
20

To jest stare, ale nadal jest hitem, gdy ktoś wygoogluje „R shiny save ggplot”, więc przedstawię inne obejście. Bardzo proste ... wywołaj ggsave w tej samej funkcji, która wyświetla wykres, co zapisze wykres jako plik na serwerze.

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

Następnie użyj downloadHandler i użyj, file.copy()aby zapisać dane z istniejącego pliku do parametru „plik”.

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

Pracuje dla mnie.

vocaloidict
źródło