Sam Sam - 10 days ago 4
R Question

Shiny: dynamic dataframe construction; renderUI, observe, reactiveValues

i think the question of how to dynamically subset data using Shiny's renderUI functionality comes up every so often but i'm struggling to understand when to use renderUI (with uiOutput) over other functionality including observe, reactive, reactiveValues and even conditionalPanel.

I would like to build a fully interactive dataframe in which every choice influences what you can choose in the other inputs. For this example we will have a selectInput (single), selectInput (multi) and a sliderInput (with max & min).

The primary select, Category 1, can be any value in Category 1 or "All". By selecting something in Category 1, you in turn influence what can be shown in Category 2 and the range of values on the slider. By choosing Category 2 first, you restrict the options of Category 1 and also the values in the slider (which always represent the max and min of the subset). By starting with the sliderInput, Category 1 and Category 2 are limited if none of their data fall in the range you have stated.

N.B. i will incorporate a 'reset' actionButton to reset the dataframe to what it is when the app initialises.

an attempt using some dummy data, which I have not managed to get working. This will subset by Category 1 and update the slider but changing Category 2 will not overwrite the rendered table;

require(shiny)

data <- data.frame(Category1 = rep(letters[1:3],each=15),
Info = paste("Text info",1:45),
Category2 = sample(letters[15:20],45,replace=T),
Size = sample(1:100, 45),
MoreStuff = paste("More Stuff",1:45))

ui <- fluidPage(

titlePanel("Test Explorer"),

sidebarLayout(
sidebarPanel(
uiOutput("category1"),
uiOutput("category2"),
uiOutput("sizeslider")
),
mainPanel(
tableOutput("table")
)
)
)

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

df_subset <- reactive({
if(input$cat1=="All") {df_subset <- data}
else{df_subset <- data[data$Category1==input$cat1,]}

})

df_subset1 <- reactive({
if(is.null(input$cat2)){df_subset()} else {df_subset()[df_subset()$Category2==input$cat2,]}
})

output$category1 <- renderUI({
selectizeInput('cat1', 'Choose Cat 1', choices = c("All",sort(as.character(unique(data$Category1)))),selected = "All")
})

output$category2 <- renderUI({
selectizeInput('cat2', 'Choose Cat 2 (optional):', choices = sort(as.character(unique(df_subset1()$Category2))), multiple = TRUE,options=NULL)
})

output$sizeslider <- renderUI({
sliderInput("size", label = "Size Range", min=min(df_subset1()$Size), max=max(df_subset1()$Size), value = c(min(df_subset1()$Size),max(df_subset1()$Size)))
})

output$table <- renderTable({
df_subset1()
})

}

shinyApp(ui, server)

Answer

A bit tricky to follow what is happening, however, is this what you want? Note changed the reactive into eventReactive so it is specifically bound to an input. Also I used the %in% operator to have multiple selects for your selectizeInput

Edit: in your first code you dont even use the slider, I added it to filtering

#rm(list = ls())
library(shiny)

data <- data.frame(Category1 = rep(letters[1:3],each=15),
                   Info = paste("Text info",1:45),
                   Category2 = sample(letters[15:20],45,replace=T),
                   Size = sample(1:100, 45),
                   MoreStuff = paste("More Stuff",1:45))

ui <- fluidPage(

  titlePanel("Test Explorer"),
  sidebarLayout(
    sidebarPanel(
      uiOutput("category1"),
      uiOutput("category2"),
      uiOutput("sizeslider")
    ),
    mainPanel(
      tableOutput("table")
    )
  )
)

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

  output$category1 <- renderUI({
    selectizeInput('cat1', 'Choose Cat 1', choices = c("All",sort(as.character(unique(data$Category1)))),selected = "All")
  })

  df_subset <- eventReactive(input$cat1,{
    if(input$cat1=="All") {df_subset <- data}
    else{df_subset <- data[data$Category1 == input$cat1,]}
  })

  df_subset1 <- reactive({
    if(is.null(input$cat2)){df_subset()} else {df_subset()[df_subset()$Category2 %in% input$cat2,]}
  })

  output$category2 <- renderUI({
    selectizeInput('cat2', 'Choose Cat 2 (optional):', choices = sort(as.character(unique(df_subset()$Category2))), multiple = TRUE,options=NULL)
  })

  output$sizeslider <- renderUI({
    sliderInput("size", label = "Size Range", min=min(df_subset1()$Size), max=max(df_subset1()$Size), value = c(min(df_subset1()$Size),max(df_subset1()$Size)))
  })

  df_subset2 <- reactive({
    if(is.null(input$size)){df_subset1()} else {df_subset1()[df_subset1()$Size >= input$size[1] & df_subset1()$Size <= input$size[2],]}
  })

  output$table <- renderTable({
    df_subset2()
  })
}

shinyApp(ui, server)

enter image description here