5

I am trying to create an application to do exploratory analysis of simulation results datasets that are always in the same (csv) format: first column with run number, several columns that contain the input parameters, one column with the timestep and then several columns that contain the values of interest. The number of input parameters and output values change but the column names that separate these sections are always the same.

Typical data looks like:

[run number],capital,weekly,[step],report1
1,10000,100,0,0
1,10000,100,1,2
1,10000,100,2,3
1,10000,100,3,3

I want the user to be able to select a subset of simulation runs to analyse, using sliders over the input parameters. This means that I need to create the appropriate number of sliders, one for each parameter input.

I have it reading the file and extracting the variable names, and the variables get listed properly. I also have some code working to get a single chooser with all the variables I want (inVarsChooser in code below), so the variable name construction is all correct. But I can't make it create multiple sliders (restrictRuns in code below).

ui code is:

library(shiny)

shinyUI(navbarPage("Test",

  # Choose dataset and display variables
  tabPanel("Input Data",
           sidebarLayout(

             sidebarPanel(
               uiOutput("restrictRuns"),
               br(),
               htmlOutput("inVarsChooser")
             ),

             mainPanel(
               fileInput(inputId = "bsFilename",
                         label = "Load file (table format)",
                         accept=c('text/csv', 'text/comma-separated-values,text/plain',
                                  '.csv'),
                         width = "800px"),

               column(width = 6,
                      h4("Simulation parameters"),
                      htmlOutput("inVarsDisplay")
                      ),

               column(width = 6,
                      h4("Simulation reporters"),
                      htmlOutput("outVarsDisplay")
               )
             )
           )
  )

))

server code is:

library(shiny)

shinyServer(function(input, output, session) {

  bsData <- reactive({
    infile <- input$bsFilename
    if (is.null(infile)){
      return(NULL)      
    }
    read.csv(infile$datapath, stringsAsFactors = TRUE)
  })

  inVars <- reactive({
    df <- bsData()
    if (is.null(df)) return(NULL)
    bsVarnames <- names(df)
    inVars <- bsVarnames[(which(bsVarnames=="X.run.number.")+1):(which(bsVarnames=="X.step.")-1)]
  })

  outVars <- reactive({
    df <- bsData()
    if (is.null(df)) return(NULL)
    bsVarnames <- names(df)
    outVars <- bsVarnames[(which(bsVarnames=="X.step.")+1):length(bsVarnames)]
  })

  output$restrictRuns <- renderUI({
    for (ii in 1:length(inVars())) {
      sliderInput(inputId = paste("range", inVars()[ii], sep=""),
                  label = inVars()[ii],
                  min = 1, max = 1000, value = c(200,500))
    }
  })

  output$inVarsDisplay <- renderUI({
    HTML(paste(inVars(), collapse = '<br/>'))
  })

  output$outVarsDisplay <- renderUI({
    HTML(paste(outVars(), collapse = '<br/>'))
  })

  output$inVarsChooser <- renderUI({
    selectInput("dependent","Select ONE variable as dependent variable from:", inVars())
  })

})
JenB
  • 17,620
  • 2
  • 17
  • 45

1 Answers1

5

If you want to add sliders for all variables, no matter which one you select in restrictRuns, add this to server.R:

output$sliders <- renderUI({
  pvars <- inVars()
  lapply(seq(pvars), function(i) {
    sliderInput(inputId = paste0("range", pvars[i]),
                label = pvars[i],
                min = 1, max = 1000, value = c(200, 500))
  })

})

and this to ui.R in your sidebarPanel(...):

uiOutput("sliders")

Sidenote:

If you replace:

bsData <- reactive({
  infile <- input$bsFilename
  if (is.null(infile)){
    return(NULL)      
  }
  read.csv(infile$datapath, stringsAsFactors = TRUE)
})

With:

bsData <- reactive({
  validate(
    need(input$bsFilename, "Input a valid filepath.")
  )
  infile <- input$bsFilename
  read.csv(infile$datapath, stringsAsFactors = TRUE)
})

You can get rid of all of the if (is.null(...)) return(NULL)

mlegge
  • 6,763
  • 3
  • 40
  • 67
  • thank you. I am not entirely how this works but it does exactly what I want. I gather the trick is to use the `lapply` and have a function that creates them, instead of my attempt to do it with a `for` loop. And thanks also for cleaning up my null stuff - I am a beginner in Shiny so it's full of awkward constructions that I stole from various examples or answers and adjusted. – JenB Feb 23 '16 at 14:51
  • @JenB you are creating a list of the widgets, which `renderUI` readily accepts (they need not even be the same type of widget). You could do so with a for loop as well, but its less concise IMHO. – mlegge Feb 23 '16 at 14:56
  • okay, I had missed that `renderUI` needs a list. Thanks for the explanation. – JenB Feb 23 '16 at 14:57
  • NP, check `?renderUI`, it doesn't need to be a list (`An expression that returns a Shiny tag object, HTML, or a list of such objects.`), but a list usually works well for dynamic inputs – mlegge Feb 23 '16 at 15:00
  • Great solution! And how would you dynamically create an output vector with all `input$range[i]` values from these sliders? Something like `output$valuesVector <- reactive(c(input$slider1, input$slider2, ...))` – Bernardo Sep 19 '19 at 01:46
  • 1
    @Bernardo first off, I don't think assigning a reactive to `output$` is allowed, secondly the sliders above are going to have two values, not one, so it'd be better in a nested list, something like: `slider_values <- reactive({ purrr::map(seq(inVars()), ~input[[.]]) })` – mlegge Sep 26 '19 at 03:11