Home > Software design >  Shiny : impossible to do a mutate on a reactive data source (reactiveFileReader / reactivePoll)
Shiny : impossible to do a mutate on a reactive data source (reactiveFileReader / reactivePoll)

Time:06-29

I'm trying to build a shiny application that gets live data from a .csv file named "test.csv". Here is a example of the data :

name   age  timestamp
Paul    23  1654095025
Michael 23  1674095025
Jane    54  1654895025
Jack    31  1954095025
Luis    23  1954095025
Anna    85  1954095025

On this csv file, I would like to do some dplyr operations, like a mutate on a timestamp to convert it to datetime. To retrieve live data, I use the shiny reactiveFileReader function. Here is the code :

library(shiny)
library(shinydashboard)
library(dplyr)

ui <- fluidPage(
        mainPanel(
           tableOutput("data")
        )
    )

server <- function(input, output) {

    fileData <- reactiveFileReader(10, NULL, 'test.csv', read.csv2)
    
    output$data <- renderTable({
      fileData() %>%
        mutate(timestamp2 = as_datetime(timestamp))
    })
}

shinyApp(ui = ui, server = server)

The problem is that the mutate is not taken into account and I can't do the operation on the timestamp field, here is a copy of the table displayed in the shiny app (result of the code above) :

name    age timestamp   timestamp2
Paul    23  1654095025  1654095025.00
Michael 23  1674095025  1674095025.00
Jane    54  1654895025  1654895025.00
Jack    31  1954095025  1954095025.00
Luis    23  1954095025  1954095025.00
Anna    85  1954095025  1954095025.00

Do you know how to solve this problem? Despite my research, I have not found anyone with a similar problem.

Note: this is actually a simplified example. I'm trying to do this in a more complex shiny application with a live connection to a mongoDB database. For that, I use reactivePoll() instead of reactiveFileReader(), but the problem is the same!

Thank you

CodePudding user response:

library(shiny)
library(dplyr)

f <- tempfile(fileext = ".csv")

writeLines(text =
'name   age  timestamp
Paul    23  1654095025
Michael 23  1674095025
Jane    54  1654895025
Jack    31  1954095025
Luis    23  1954095025
Anna    85  1954095025
', con = f)

ui <- fluidPage(
  mainPanel(
    tableOutput("data")
  )
)

server <- function(input, output, session) {
  
  fileData <- reactivePoll(1000, session,
    checkFunc = function() {
      info <- file.info(f)
      return(paste(f, info$mtime, info$size))
    }, 
    valueFunc = function() {
      read.table(f, header = TRUE)
    })
  
  output$data <- renderTable({
    fileData() %>%
      mutate(timestamp2 = as.character(lubridate::as_datetime(timestamp)))
  })
}

shinyApp(ui = ui, server = server)

CodePudding user response:

Thank you for your feedback Aurèle. I think I have found the source of the problem, but I don't know how to solve it. The error I had seemed strange: I was told that a select or mutate operation was not possible on the data because the field did not exist but yet, the operation was done correctly!

In fact in my shiny, I use a pickerInput that allows the user to select the database he wants to work on (in your code above, it's as if we had two tables and could choose which one to retrieve from the reactivePoll using the pickerInput selection). This picker input must be reactive : if a database is added, the picker input must display the new database in the list (as a reminder, I work with mongoDB that I request with the mongolite package). To make the pickerInput reactive, I use updatePickerInput() with an observe().

I found that if I removed the updatePickerInput and put the databases names by hand in the choices argument of the pickerInput, the warnings disappeared! I'm not an expert, but I have the impression that when the application is opened, the "test" reactive value is executed before the pickerInput has time to retrieve the information. That's why shiny tells me that it can't find the data and displays the warning.

However, I have not yet managed to solve this problem.

I know it's quite heavy, but to help, here's an example of the code I use:

library(shiny)
library(shinydashboard)
library(shinyWidgets)
library(mongolite)
library(lubridate)
library(tidyverse)



#### UI 

ui <- dashboardPage(
  header = dashboardHeader(),
  sidebar = dashboardSidebar(),
  body = dashboardBody(
    pickerInput(
    inputId = "database_selection",
    choices = NULL, 
    selected = NULL, 
    multiple = FALSE
    ),
    
    hr(),
    
    dataTableOutput("table1")
  )
)


#### SERVER

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

# GET LIVE DATABASES LIST ------------------------------------------------------

# get the databases list from server. Update every 10 ms to check 
# if new database is added or deleted
databases_list <- reactivePoll(
  
  intervalMillis = 10,
  
  session = NULL,

  valueFunc = function(){
    admin <- mongo(db = "admin", url = YOUR_MONGDB_URL)
    databasesinfos_list <- admin$run('{"listDatabases":1}')
    databasesinfos_table <- databasesinfos_list[["databases"]] %>%
      filter(startsWith(name, "LEXIM"))
    databasesinfos_vector <- sort(c(databasesinfos_table$name))
    return(databasesinfos_vector)},

  checkFunc = function(){
    admin <- mongo(db = "admin", url = YOUR_MONGDB_URL)
    databasesinfos_list <- admin$run('{"listDatabases":1}')
    databasesinfos_table <- databasesinfos_list[["databases"]] %>%
      filter(startsWith(name, "LEXIM"))
    return(nrow(databasesinfos_table))}
)

# Allows to propose to the user the live list of the existing databases in a 
# picker input menu. 
observe({
  updatePickerInput(
    session, 
    "database_selection",
    choices = databases_list(),
    selected = databases_list()[1]
  )
})

# get the database chosen by the user in the picker input for the analysis.
database_selected <- reactive(
  paste(input$database_selection)
)

# GET DATABASES DATA -----------------------------------------------------------------

# gets the data from the "my_collection" collection for the selected database 
# Updates every 10 ms to check if new data is added

my_collection_poll <- reactivePoll(
  intervalMillis = 500,
  session = NULL,
  # function that looks if the "my_collection" collection has changed
  checkFunc = function(){
    my_collection <- mongo(
      collection = "my_collection", 
      db = database_selected(),
      url = YOUR_MONGDB_URL)
    return(nrow(my_collection$find('{}')))},
  # function that fetches the data from the "my_collection" collection if it changes
  valueFunc = function(){
    my_collection <- mongo(
      collection = "my_collection", 
      db = database_selected(),   
      url = YOUR_MONGDB_URL)
    return(my_collection$find('{}'))
  }
)



test <- reactive({
  tmp <- my_collection_poll()
  tmp <- tmp %>% 
    mutate(timestamp = as.character(as_datetime(timestamp))) 
  return(tmp)
})

# DISPLAY THE TABLE ------------------------------------------------------------

output$table1 <- renderDataTable({
  test()
})



}


shinyApp(
  ui,
  server
)

  • Related