Download dataset filtered in Shiny Input

Hi,

I'm trying to create a way to download a csv of a table that is filtered through a Shiny input. So for example, when I filter the iris dataset to only have the versicolor Species, I would like to download a csv that only includes versicolor. There are two ways to download csvs - both of which do not give me exactly what I want.

  1. The DT buttons extension only downloads what is visible - for huge csvs this is not optimal
  2. The downloadHandler - this downloads the entire dataset and does not keep the filters introduced in the input.

My last attempt was to make the data itself reactive and this also did not work. I have attached a reprex below and would very much appreciate the help.

The eventual use case is to deploy a Shiny app for very large datasets (~50K rows) that folks can filter and then download as a csv.

library(tidyverse)
library(shiny)
library(DT)
#> 
#> Attaching package: 'DT'
#> The following objects are masked from 'package:shiny':
#> 
#>     dataTableOutput, renderDataTable


species <- iris %>% select(Species) %>% distinct() %>% pull()


# shiny app 
ui <- fluidPage(
                h1("Data Download Dashboard"),
                sidebarLayout(
                  sidebarPanel(selectInput("Species", label = "Choose species", species, multiple = TRUE, selected = species),
                               downloadButton("download1","Download entire Table  as csv")),
                  mainPanel(h4("Table 1: Iris"),
                            dataTableOutput("iris_dto")
                            )
                ))

server <- function(input, output, session) {
  
  thedata <- reactive(iris)
  
  output$iris_dto <- renderDataTable({
    thedata() %>% filter(Species == input$Species) %>% 
      datatable(extensions = 'Buttons',
                options = list(
                  #Each letter is a dif element of a datatable view, this makes buttons the last thing that's shown.
                  dom = 'lfrtipB',
                  buttons = c("copy", "csv", "pdf")),
                filter = list(
                  position = 'top'),
                rownames = FALSE)
  })
  
 
  output$download1 <- downloadHandler(
    filename = function() {
      paste("iris_", Sys.Date(), ".csv", sep="")
    },
    content = function(file) {
      write.csv(thedata(), file)
    }
  )
  
 
  
}

shinyApp(ui, server)
#> PhantomJS not found. You can install it with webshot::install_phantomjs(). If it is installed, please make sure the phantomjs executable can be found via the PATH variable.
2 Likes

Hi Rika,

You've basically got it done :slight_smile: The only thing you need to change is to make sure that the filtering is happening within the thedata reactive expression, so what gets fed into downloadHandler is the filtered data. So you refer to thedata() within both renderDataTable and downloadHandler.

Here is your code with that change:

library(tidyverse)
library(shiny)
library(DT)
#> 
#> Attaching package: 'DT'
#> The following objects are masked from 'package:shiny':
#> 
#>     dataTableOutput, renderDataTable


species <- iris %>% select(Species) %>% distinct() %>% pull()


# shiny app 
ui <- fluidPage(
  h1("Data Download Dashboard"),
  sidebarLayout(
    sidebarPanel(selectInput("Species", label = "Choose species", species, multiple = TRUE, selected = species),
                 downloadButton("download1","Download entire Table  as csv")),
    mainPanel(h4("Table 1: Iris"),
              dataTableOutput("iris_dto")
    )
  ))

server <- function(input, output, session) {
  
  thedata <- reactive({
    iris %>% 
      filter(Species == input$Species)
    })
  
  output$iris_dto <- renderDataTable({
    thedata()  %>% 
      datatable(extensions = 'Buttons',
                options = list(
                  #Each letter is a dif element of a datatable view, this makes buttons the last thing that's shown.
                  dom = 'lfrtipB',
                  buttons = c("copy", "csv", "pdf")),
                filter = list(
                  position = 'top'),
                rownames = FALSE)
  })
  
  
  output$download1 <- downloadHandler(
    filename = function() {
      paste("iris_", Sys.Date(), ".csv", sep="")
    },
    content = function(file) {
      write.csv(thedata(), file)
    }
  )
1 Like

Thank you soooo much! This was exactly what I needed. For others who may also be in my position, also want to call out that my code didn't have the brackets in the reactive expression which leads to the following error.
Error in UseMethod("filter_") : no applicable method for 'filter_' applied to an object of class "c('reactiveExpr', 'reactive', 'function')"

Hlynur's solution does it correctly though! :smile: Thanks!

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.