as.formula function call where formula inputs are shiny reactive objects

208 Views Asked by At

This has been bugging me for ages.

I have a function where the first argument either needs to be of the form function(data~ group_variable) OR of the form function(data, group = data$group_variable).

I have this function running smoothly in the console, but it is integral to my shiny app and it has been bugging me for ages, because both the data and the group are user-selected reactive objects, so it needs to take the form:

function(datasetInput() ~ !!input$group_variable) or some version thereof.

I haven't been able to find any combination of !!s, enquo(), substitute(), as.function(substitute()), etc that will make this work within the shiny app. as.Formula(substitute(data ~ group)) works in the console.

here is as minimal as I can make a reprex:

library(shiny)
library(shinyWidgets)
library(psych)
library(dplyr)
library(gt)
use <- function(name) {
  # consider future support for .json? 
  if (grepl(".csv", name)) {
    readr::read_csv(name)
  } else if (grepl(".xlsx", name)) {
    readxl::read_xlsx(name)
  } else if (grepl(".dta", name)) {
    haven::read_dta(name)
  } else if (grepl(".sav", name)) {
    haven::read_spss(name)
  } else if (grepl(".rda", name)) {
    load(name)
  } else {
    stop("unknown data type.")
  }
}

ui <- fluidPage(
  mainPanel(
    fileInput("FileInput", "Input Your Data Set"),
    helpText("Dataset must be one of: .csv, .sav, .dta, .xlsx, or .rda"), 
    materialSwitch(
      inputId = "ext_desc",
      label = "Extended Description", 
      value = FALSE,
      status = "primary"
    ),
    materialSwitch(
      inputId = "desc_by_group_bool",
      label = "Describe By A Group", 
      value = FALSE, 
      status = "primary"
    ),
    varSelectInput(
      inputId = "desc_group",
      label = "Select A Group",
      data = NULL, 
      width = "400px"
    ),
    gt::gt_output("description")
    
  )
)
server <- function(input,output, session){
  
  datasetInput <- reactive({
    infile <- input$FileInput
    if (is.null(infile))
      return(NULL)
    dat<-use(infile$datapath)
    names(dat) <-  gsub(" ", "_", names(dat), fixed = TRUE) 
    return(dat)
  })
  
  observeEvent(datasetInput(), {
    updateVarSelectInput(session, "desc_group", data = datasetInput())
  })
  
  desc <- reactive({
    req(datasetInput())
    if (input$desc_by_group_bool == FALSE) {
      datasetInput() %>%
        #select_if(is_numeric) %>%
        psych::describe(., fast = !(input$ext_desc),
                        omit = TRUE) %>%
        add_rownames(var = "Variable") %>%
        dplyr::select(-c(vars)) %>%
        dplyr::mutate(dplyr::across(is.numeric, round, 2)) %>%
        gt::gt() %>%
        gt::tab_options(
          column_labels.font.size = "small",
          table.font.size = "small",
          row_group.font.size = "small",
          data_row.padding = px(3)
        ) %>%
        gt::tab_header(title = paste0("Data Description"))
    } else {
      # datasetInput() %>%
      # select_if(is.numeric) %>%
      psych::describeBy( datasetInput() ~ !!input$desc_group,
                         # here we get "invalid argument type" error
                         fast = !(input$ext_desc),
                         mat = TRUE) %>%
        tibble::rownames_to_column() %>%
        select(-c(item, vars)) %>%
        dplyr::mutate(dplyr::across(is.numeric, round, 2)) %>%
        arrange(group1) %>%
        group_by(group1) %>%
        gt() %>%
        gt::tab_options(
          column_labels.font.size = "small",
          table.font.size = "small",
          row_group.font.size = "small",
          data_row.padding = px(3)
        ) %>%
        tab_header(title = paste0("Data Description") ,
                   subtitle = paste0("Grouped by: ",  input$desc_group)
        )
    }
    
    
  })
  
  output$description =  gt::render_gt(desc())
  
}
shinyApp(ui = ui, server = server)

The line causing the error--and the source of my question, is, forgive me, line 85 above.

1

There are 1 best solutions below

0
On BEST ANSWER

There are various ways we can solve this. One way would be to use [[]] to subset the specific column. So change the describeBy line to :

psych::describeBy( datasetInput(), group = datasetInput()[[input$desc_group]]

Also add where in dplyr::across

dplyr::mutate(dplyr::across(where(is.numeric), round, 2))