Nckh
Nckh

Reputation: 67

RShiny: Call file based on value in input field

I want to use different datasets in my app depending on the user's input.

Imagine I have these files:

AA.csv 
AB.csv
AC.csv 

Is it possible to formulate a conditional statement that changes the file name to be called according to the selection of the user in the input field?

For illustration purposes, I'm attaching some code of the behaviour that I'd like to have.

ui<- fluidPage(
  selectInput(inputId="file", label= "File",
              choices = c("AA", "AB", "AC"))
)
server<- function(input, output,session){

datafile<- reactive({read.csv("input$file".csv })

I hope my question is clear.

Upvotes: 0

Views: 33

Answers (1)

r2evans
r2evans

Reputation: 160397

The first step is that your read.csv code should be something like

datafile <- reactive({ read.csv(paste0(input$file, ".csv")) })

Two ways to make this a little better:

  1. Add req, so that a missing (mislocated) file doesn't spawn an error, just a lack of data; and
  2. Make this dynamic so that your project can include an arbitrary list of data files, and you can add a file and not have to edit code to change the selectInput.

If you're only concerned about checking for the available files at app startup, then perhaps this will suffice:

files <- list.files("./data", pattern = "\\.csv$", full.names = TRUE)
choices <- setNames(files, tools:::file_path_sans_ext(basename(files)))
shinyApp(
  ui = fluidPage(
    selectInput("file", "File", choices = choices, selectize = FALSE),
    textAreaInput("txtarea", "File top 10 lines:", rows = 10)
  ),
  server = function(input, output, session) {
    somedata <- reactive({
      req(input$file)
      readLines(input$file, n = 10)
    })
    observeEvent(somedata(), {
      updateTextAreaInput(session, "txtarea", value = somedata())
    })
  }
)

(I'm using a textAreaInput just to show the file contents ... assuming you have different needs, then you likely don't need the observeEvent in order to update that input field.)

If this is a "long-running" app that needs to react to new files without restarting, then you can set a "poll" that will periodically update the list of available files for input. While this is intended to adjust for new files, it also reacts to removed files.

shinyApp(
  ui = fluidPage(
    selectInput("file", "File", choices = c(), selectize = FALSE),
    textAreaInput("txtarea", "File top 10 lines:", rows = 10)
  ),
  server = function(input, output, session) {
    observe({
      invalidateLater(30000, session) # 30 seconds
      sel <- input$file
      files <- list.files("./data", pattern = "\\.csv$", full.names = TRUE)
      choices <- setNames(files, tools:::file_path_sans_ext(basename(files)))
      if (!sel %in% choices) sel <- choices[1]
      updateSelectInput(session, "file", choices = choices, selected = sel)
    })
    somedata <- reactive({
      req(input$file)
      readLines(input$file, n = 10)
    })
    observeEvent(somedata(), {
      updateTextAreaInput(session, "txtarea", value = somedata())
    })
  }
)

This second app starts with zero choices (c()) and updates them immediately. I set a 30-second poll; your use-case will dictate something more reasonable, recognizing that sub-second response might be compromising unnecessary filesystem access for apparent need for file-reactivity.

I did a quick setup for testing with:

# setup
dir.create("data")
writeLines("hello world", "./data/AA.csv")
writeLines("hello world again", "./data/AB.csv")

Upvotes: 1

Related Questions