Dick McManus
Dick McManus

Reputation: 789

Use results of selection to set options for second conditional selection input with R Shiny

I am trying to make a Shiny tool to create histograms based on the selection of a dateframe and then column in the dataframe. However, I am having a hard time getting the UI to read the columns from a selected dataframe to then present the column options for selection.

For example we have DataFrameA with two columns cl1 and cl2, and then DataFrameB with columns clx, cly, clz. If I select DataFrameA, the next selectInput ui should allow me to select the columns cl! or cl2, and if I had selected DataFrameB it would give me the options of clx, cly, clz.

ui = fluidPage(
     selectInput(inputId = "dataFrame",
          label = "Select Dataframe",
          choices = names(which(unlist(eapply(.GlobalEnv,is.data.frame))))
          ),

  #This is where I get fudged, I want the dataframe name to have been read 
  #in the server now, and used to select the column names so I can now 
  #display them as select options. 

  conditionalPanel(condition = "output.columnChoices",
               selectInput(inputId = "dataColumn",
                           label = "Choose Column:",
                           choices = "output.columnChoices")
               )
  )

 server <- function(input,output) {

 #To my understanding this should reactively read in the dataframe name
 #selected and output the list of column names for that dataframe.

 output$columnChoices <- reactive({
    colnames(input$DataFrame)
 })

 outputOptions(output, "columnChoices", suspendWhenHidden = FALSE)  

 }

shinyApp(ui, server)

When I run it, it almost looks like it produces both options and then instantly shifts to only the dataframe input option. Any suggestions? In the end I'd like to produce a histogram based on the column selection but I need to figure this out first.

Upvotes: 1

Views: 668

Answers (2)

fenix_fx
fenix_fx

Reputation: 394

You can use

uiOutput

You place uiOutput("ui_id") in ui section and create whatever you want from server with output$ui_id <- renderUI(). I think code would be like this (i used dirty hack by evaluating name from input to get dataframe, also i delete conditionalPanel because we manage it manually in server):

ui = fluidPage(
  selectInput(
    inputId = "dataFrame",
    label = "Select Dataframe",
    choices = names(which(unlist(eapply(.GlobalEnv,is.data.frame))))
  ),

  uiOutput("dataColumnInput")
)

server <- function(input,output) {

  output$dataColumnInput <- renderUI({
    tagList(
      selectInput(
        inputId = "dataColumn",
        label = "Choose Column:",
        choices = colnames(eval(parse(text = input$dataFrame)))
      )
    )
  })

}

shinyApp(ui, server)

Also, you can use

updateSelectInput

for normal input in UI and configure that from server how you need.

UPD: But if you will use pre-known dataframes, better and simpler will be use switch with dataframes (instead evaluating), i think it can look like this:

   switch(
      input$dataFrame,
      "test1" = test1,
      "test2" = test2)

Upvotes: 0

Florian
Florian

Reputation: 25385

You can use updateSelectInput to update your input with the new column names, and you can use get() to fetch a dataframe from the environment.


enter image description here


Working example:

A = data.frame(a=letters[1:5],b=letters[6:10],c=letters[11:15])
B = data.frame(x=LETTERS[1:5],y=LETTERS[6:10],z=LETTERS[11:15])

library(shiny)

ui = fluidPage(
  selectInput(inputId = "dataFrame",
              label = "Select Dataframe",
              choices = names(which(unlist(eapply(.GlobalEnv,is.data.frame))))
  ),
  selectInput(inputId = "dataColumn",
              label = "Choose Column:",
              choices = NULL),
  textOutput('test')
)

server <- function(input,output,session) {

  observeEvent(input$dataFrame,{
    updateSelectInput(session,'dataColumn',choices = colnames(get(input$dataFrame)))
  })

  output$test = renderText({
    df = get(input$dataFrame)
    text = paste(df[[input$dataColumn]],sep=', ')
  })
}

shinyApp(ui, server)

However, I would advise you to store the data.frames in a list, and use that list in your app:

A = data.frame(a=letters[1:5],b=letters[6:10],c=letters[11:15])
B = data.frame(x=LETTERS[1:5],y=LETTERS[6:10],z=LETTERS[11:15])
my_dfs = list(A=A,B=B)

library(shiny)

ui = fluidPage(
  selectInput(inputId = "dataFrame",
              label = "Select Dataframe",
              choices = names(my_dfs)
  ),
  selectInput(inputId = "dataColumn",
              label = "Choose Column:",
              choices = NULL),
  textOutput('test')
)

server <- function(input,output,session) {

  observeEvent(input$dataFrame,{
    updateSelectInput(session,'dataColumn',choices = colnames(my_dfs[[input$dataFrame]]))
  })

  output$test = renderText({
    df = my_dfs[[input$dataFrame]]
    text = paste(df[[input$dataColumn]],sep=', ')
  })
}

shinyApp(ui, server)

Hope this helps!

Upvotes: 1

Related Questions