Search code examples
rshinyselectinput

R Shiny - Select City depend on selected Province with selectInput


I am trying to subset a data frame base on the Province column and the City column. In shiny, I want to let the user choose the province then the city, with the selectInput UI.

Heres what the data frame looks like.
ColumnInfoTemp[2] is the city, InfoTemp[3] is the province.
The dataset is big, they actually have many levels.

Year  Autumn    InfoTemp[2] InfoTemp[3]  
1913     8.9 SHAWNIGAN LAKE          BC  
1914     9.5 SHAWNIGAN LAKE          BC  
1915     9.3 SHAWNIGAN LAKE          BC  
1916     8.5 SHAWNIGAN LAKE          BC  
1917     9.9 SHAWNIGAN LAKE          BC  
1918 -9999.9 SHAWNIGAN LAKE          BC  

Ultimately, this is a plot (for a city) I am planning to go.

Here is the code so far, did not do anything...

server.R

library(shiny)

shinyServer(function(input, output) {

  #MeanTemp
  load("CanadianMeanTemp.Rdata")

  province = input$provinces
  city = input$cities

  output$distPlot <- renderPlot({

    MeanTemp_province = MeanTemp[grep(c(province), MeanTemp$`InfoTemp[3]`),]
    MeanTemp_city = MeanTemp_province[grep(c(city), MeanTemp$`InfoTemp[2]`),]

    plot(MeanTemp_city$Year, MeanTemp_city$Annual, type = "l")
    lines(supsmu(MeanTemp_city$Year, MeanTemp_city$Annual), col = 2)
  })

})

ui.R

library(shiny)
shinyUI(fluidPage(
  titlePanel("Temperature"),

  sidebarLayout(
    sidebarPanel(
      selectInput('provinces', 'Province', choices = levels(MeanTemp$`InfoTemp[3]`)),

      conditionalPanel(
        condition = "input.provinces == true",
        selectInput('cities', 'City', choices = levels(MeanTemp_province$`InfoTemp[2]`))
      )
    ),

    mainPanel(
      plotOutput("distPlot")
    )
  )

))


Solution

  • Code

    library(shiny)
    
    city_data <- expand.grid(Year = seq.Date(as.Date("1990/1/1"), 
                                             as.Date("2000/1/1"), by = "year"),
                             Province = LETTERS[1:3],
                             City_Counter = 1:4)
    
    city_data$City <- paste(city_data$Province, city_data$City_Counter, sep = "_")
    city_data$Autumn <- runif(NROW(city_data), 10, 100)
    
    ui <- fluidPage(
      selectInput("province", "Select province:", unique(city_data$Province)),
      selectInput("city", "Select city:", unique(city_data$City)),
      plotOutput("plot")
    )
    
    server <- function(input, output, session) {
      get_filtered_data <- reactive({
         city_data[city_data$Province %in% input$province & 
                   city_data$City %in% input$city, , drop = FALSE]
      })
    
      observe({
         updateSelectInput(session, 
                           "city",
                           choices = unique(city_data[city_data$Province %in%
                                                      input$province, "City"]))
      })
    
      output$plot <- renderPlot({
        my_data <- get_filtered_data()
        req(NROW(my_data) > 0)
        plot(my_data$Year, my_data$Autumn, type = "l")
      })
    }
    
    shinyApp(ui, server)
    

    Some Remarks

    • I used updateSelectInput instead of an uiOutput approach, b/c I feel it is an overkill to re-render the whole input when in the end only the choices change.
    • I used base R here for subsetting, but this could be easily replaced by tidyversesyntax if needed
    • I used %in% instead of == in the filtering, in order to allow for extending that to show multiple Cities / Provinces in facetted graphs if needed