0

As a first class project in R, I am trying to build a Shiny app to display Paris Metro lines, stops and schedule.

I successfully managed to create a dynamic map that show stops based on selected line. I'd like now to display selected stop schedule based on selected hour. I am having issues to update the data and to display it. So far my logic is fine as it is working manually in an R script, but in the App I don't know how to display it in a table.

As you can see in the screenshot below, the table has the right header but no data inside.

How could I turn this around ?

Here is my code:

library(shiny)
library(dplyr)
library(tidyverse)
library(leaflet)
library(DT)
library(lubridate)

## Files
routes <- readRDS("~/Desktop/EDHEC/Courses Nicolas/Msc DAAI/S1/R Programming/Project/RATP Data/rdsnew/routesnew.rds")
trips <- readRDS("~/Desktop/EDHEC/Courses Nicolas/Msc DAAI/S1/R Programming/Project/RATP Data/rdsnew/tripsnew.rds")
stop_times <- readRDS("~/Desktop/EDHEC/Courses Nicolas/Msc DAAI/S1/R Programming/Project/RATP Data/rdsnew/stoptimesnew.rds")
stops <- readRDS("~/Desktop/EDHEC/Courses Nicolas/Msc DAAI/S1/R Programming/Project/RATP Data/rdsnew/stopsnew.rds")
calendar <- readRDS("~/Desktop/EDHEC/Courses Nicolas/Msc DAAI/S1/R Programming/Project/RATP Data/rds/calendar_dates.rds")
terminus <- readRDS("~/Desktop/EDHEC/Courses Nicolas/Msc DAAI/S1/R Programming/Project/RATP Data/terminus.rds")

## Convert strings to Date
calendar$date <- as.Date(as.character(calendar$date),"%Y%m%d")
stop_times$arrival_time <- strptime(stop_times$arrival_time,format = "%H:%M:%S")

## Keep Today's data
calendar <- calendar[calendar$date == Sys.Date(),]

# Define UI for application that draws a histogram
ui <- fluidPage(

    # Application title
    titlePanel("Paris Metro Schedule"),

    # Sidebar with a slider input for number of bins 
    sidebarLayout(
        sidebarPanel(
          selectInput(inputId = "Line", label = "Select your line", choices = c(1,2,3,4,5,6,7,'7B',8,9,10,11,12,13,14)),
          selectInput(inputId = "Direction", label = "Select your direction", choices = NULL),
          selectInput(inputId = "Stop", label = "Select your stop", choices = NULL),
          selectInput(inputId = "Hour", label = "Select your hour of departure", choices = c(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23))
        ),

        # Show a plot of the generated distribution
        mainPanel(
          leafletOutput("map"),
          tableOutput("table")
        )
    )
)


# Define server logic required to draw a histogram
server <- function(input, output,session) {
  
  
  #Create the map
  output$map <- renderLeaflet({
    leaflet() %>%
      addProviderTiles(providers$CartoDB.Positron) %>%
      setView(lng = 2.34, lat = 48.86, zoom = 12) %>%
      addMarkers(lng = stops$stop_lon, lat = stops$stop_lat, label = stops$stop_name)
  })
  
  #Set the values that will appear in the schedule table / terminus is set as the initial values but will be replaced
  schedule <- reactiveValues()
  schedule$df <- terminus 
  
  # Reactive to adjust inputs choices, map and schedule table data
  observe({
      
    #Logic to isolate the data to render based on inputs selection
      routes.kept <- routes[routes$route_short_name %in% input$Line,]
      terminus.kept <- terminus[terminus$route_short_name %in% input$Line,]
      direction.number <- terminus[terminus$Terminus %in% terminus.kept$Terminus,]
      trips.kept <- trips[trips$route_id %in% routes.kept$route_id & trips$service_id %in% calendar$service_id & trips$direction_id %in% direction.number$route_desc,]
      stop_times.kept <- stop_times[stop_times$trip_id %in% trips.kept$trip_id,]
      stops.kept <- stops[stops$stop_id %in% stop_times.kept$stop_id,]
      
    #Reactive inputs based on the first one
      choices_Stop <- stops.kept$stop_name %>% unique() %>% sort()
      updateSelectInput(session, "Stop", choices = choices_Stop)
      updateSelectInput(session, "Direction", choices = terminus.kept$Terminus)
    
    #Stop times to render in a table 
      schedule$df <- stop_times.kept[stop_times.kept$stop_id %in% input$Stop,] %>% select(arrival_time) %>% filter((hour(arrival_time) == input$Hour)) %>% arrange(arrival_time)
     
    #Update the map 
      leafletProxy("map") %>%
        clearMarkers() %>%
        addMarkers(lng = stops.kept$stop_lon, lat = stops.kept$stop_lat, label = stops.kept$stop_name)
    
   })

  # Create the table with schedule for specific stop and hour
  output$table <- renderTable(schedule$df)

}

# Run the application 
shinyApp(ui = ui, server = server)

Screenshot of the app UI

Thank you very much for your help!! Best,

1 Answers1

0

The arrival_time column is displayed, so that probably means the error isn't actually in the rendering. Most likely, the issue has to do with your filtering.

If you run your code through the debugger in RStudio, you can see that this line of your code:

stop_times.kept[stop_times.kept$stop_id %in% input$Stop,]

produces 0 cases, which is why your table looks empty. I think you set up the subsetting wrong here (you have set it to look for matches for stop_id, which is an integer, but the user input from input$Stop are the stop names, which are characters). If you fix this, the table output should be fixed!

justincj
  • 58
  • 1
  • 7
  • I'm not entirely sure if this is the issue since I can't run the app on my machine. If that doesn't solve it, if you could provide a representative sample dataset (can be as minimal as possible) we could use to run the app on our own machines, we could probably help you troubleshoot a lot better! – justincj Nov 03 '20 at 17:11
  • Thank you for the documentation. I tried to place renderTable inside the observe without any result as well.. To be honest I tried a couple of things such as dataTableProxy or observeEvent without success.. – Nicolas Meunier Nov 03 '20 at 17:37
  • Here you can find the full datasets : https://drive.google.com/drive/folders/17hGsxYUnH1gRUAt4zq2CvHLwq_4h6qUG?usp=sharing Thank you very much! – Nicolas Meunier Nov 03 '20 at 17:38
  • Thanks, I'll give it a shot in a little while and will get back to you. – justincj Nov 03 '20 at 18:06
  • I just tried exchanging `observe()` to `reactive()` and the table output worked for me. It can be a bit hard to see because your data is so large, so it takes a while to update. Did changing the functions still not display the table for you? – justincj Nov 03 '20 at 19:31
  • It didn't, I might have missed something.. Could you please give me the piece of code that worked for you ? – Nicolas Meunier Nov 03 '20 at 20:09
  • Changing observe() to reactive() makes the whole function not work anymore.. The table output I get is the initial one "terminus" and not "schedule" – Nicolas Meunier Nov 03 '20 at 22:21
  • Okay, I played around with it a LOT more in the debugger. Think I found the actual bug. Keep the code as `observe()` (ignore my initial suggestion), and give that a try! Let me know if you're still confused! – justincj Nov 04 '20 at 07:12
  • Hello, I played with my code a bit to change the subset adding an intermediate variable called selection. `selection <- stops.kept[stops.kept$stop_name %in% input$Stop,] schedule$df <- stop_times.kept[stop_times.kept$stop_id %in% selection$stop_id,] %>% select(arrival_time) %>% filter((hour(arrival_time) == input$Hour)) %>% arrange(arrival_time)` This seems to work but I have another issue now : I am getting this message "number of items to replace is not a multiple of replacement length" I assume it is because my initial table has not the same length as the new one, right ? Thank you – Nicolas Meunier Nov 04 '20 at 10:23
  • Yes, that error usually means there is a mismatch in vector lengths between the vector you are trying to replace and your new vector. – justincj Nov 04 '20 at 16:43
  • Any idea how I could work this out ? I need to set initial value my "schedule" and then modify it with my subset formulas inside the observe.. They will never have the same length – Nicolas Meunier Nov 04 '20 at 17:19
  • I might be misunderstanding your data, but is there any particular reason why you need to have an initial value for schedule, and then replace it? Could you not just generate your schedule from your subset formulas, instead existing data? – justincj Nov 04 '20 at 23:49
  • There is no reason actually, I thought that setting first the object and then modifying it with observe() could be a great option.. How could I visualize the schedule without setting it first ? – Nicolas Meunier Nov 05 '20 at 09:11