rshinyshiny-reactivityr-factor

Why does the as.factor() function not work in a Shiny reactive express, but works fine outside of Shiny in base R?


In the example code posted at the bottom, I get different results for the as.factor() function when running the code inside a Shiny reactive versus running it outside of Shiny. What am I doing wrong? I think I missed something basic in my Shiny lessons.

Data is the lung data from the "survival" package. Object lung_1 in my example code is generated outside of Shiny, and object lung_2 is generated inside Shiny as a reactive. Object lung_1 is correct, and object_2 is incorrect, as illustrated below. Both lung_1 and lung_2 should be the same. The below example assumes "sex" has been selected in the selectInput() as the group variable, and it should be a factor class.

enter image description here

Code:

library(shiny)
library(survival)

lung_1 <- lung %>% mutate(group = as.factor(sex))

ui <- fluidPage(
  selectInput(
    "varCon", 
    "Grouping variable:",
     c("None" = 1,
       "sex" = "sex",
       "ph.ecog" = "ph.ecog"
     )
  )
)

server <- function(input, output, session) {
  lung_2 <- reactive({lung %>% mutate(group = as.factor(input$varCon))})
  
  # throw reactive DF into global environment to peek at what's going on...
  oe <- reactive({lung_2()})
  observeEvent(oe(),{lung_2.R <<- oe()})
  
}

shinyApp(ui, server)

Solution

  • The issue you are facing is due to the difference in timing between the execution of the code outside and inside Shiny.

    When you run the code outside Shiny, the as.factor() function is executed immediately after the mutate() function. This means that the resulting group column is of class factor.

    However, when you run the code inside Shiny, the as.factor() function is executed only when the reactive expression is triggered. This means that the group column is of class character until the reactive expression is triggered and the as.factor() function is executed.

    To fix this issue, you can convert the group column to a factor inside the reactive expression, rather than using mutate().

     library(shiny)
    library(survival)
    
    lung_1 <- lung %>% mutate(group = as.factor(sex))
    
    ui <- fluidPage(
      selectInput(
        "varCon", 
        "Grouping variable:",
         c("None" = 1,
           "sex" = "sex",
           "ph.ecog" = "ph.ecog"
         )
      )
    )
    
    server <- function(input, output, session) {
      lung_2 <- reactive({
        data <- lung
        if(input$varCon != 1) {
          data$group <- data[[input$varCon]]
        } else {
          data$group <- NA_character_
        }
        data$group <- as.factor(data$group)
        return(data)
      })
      
      # throw reactive DF into global environment to peek at what's going on...
      oe <- reactive({lung_2()})
      observeEvent(oe(),{lung_2.R <<- oe()})
      
    }
    
    shinyApp(ui, server)