shinydrc

How to store model coefficients to be used as variables in a function in R Shiny?


I'm creating an app that takes a .csv file and spits out concentration values after doing some analysis on the back end. The analysis involves running a 5-parameter logistic curve using the drc() package and then storing the model coefficients to use in a function, which is where I'm running into trouble. It either doesn't recognize the argument I'm passing through the function, or it doesn't recognize the variables.

The following data can be written as a .csv file and then used in the app for a reproducible example.

smpdata <- structure(list(Sample = c("Std 0", "Std 0", "Std 1", "Std 1", 
"Std 2", "Std 2", "Std 3", "Std 3", "Std 4", "Std 4"), Absorbance = c(0.854, 
0.876, 0.736, 0.736, 0.551, 0.569, 0.46, 0.414, 0.312, 0.307), 
    Concentration = c(0, 0, 0.05, 0.05, 0.15, 0.15, 0.4, 0.4, 
    1.5, 1.5)), row.names = c(NA, 10L), class = "data.frame")


library(dplyr)
library(tidyverse)
library(drc)
library(shiny)

# function that I need to calculate concentration values
concentration <- function(y){
  
  c*(((a-d)/(y-d))^(1/m)-1)^(1/b)
}

# User uploads the .csv file

ui <- fluidPage(
  titlePanel("Microcystin Concentrations from ELISA using 5 PL Curve"),
  fluidRow(
    h4("Upload Data"),
    column(6,
           fileInput("file", "Choose CSV File",
                     multiple = F,
                     accept = c("text/csv",
                                "text/comma-separated-values,text/plain",
                                ".csv"),
                     placeholder = "CSV files only",
                     width = "100%"))),
# Results from data are shown in a table in the server
  fluidRow(
    h4("Results"),
    column(6,
           verbatimTextOutput("results"))))
)

server <- function(input, output, session) {
  
  data <- reactive({
    req(input$file)
    read.csv(input$file$datapath)
  })
  
  dt <- reactive({
    data() %>%
    filter(!Absorbance == 0) %>%
    group_by(Sample) %>%
    mutate(Mean_Absorbance = mean(Absorbance)) %>%
    distinct(Sample, .keep_all = T) %>%
    dplyr::select(Type, Sample, Mean_Absorbance, Concentration)})
  
  
  stds <- reactive({dt() %>%
      filter(Type == "Standard")})
  
  fiveplc <- reactive({drm(Mean_Absorbance ~ Concentration, data=stds(),
                 fct = LL.5(names = c("b", "d", "a", "c", "e")))})
  
  sum.fiveplc <- reactive({summary(fiveplc())})

# THIS IS WHERE THE APP STOPS WORKING
  output$results <- renderTable({
    
    b <- sum.fiveplc()$coefficients[1] # store model coefficients to variables
    d <- sum.fiveplc()$coefficients[2]
    a <- sum.fiveplc()$coefficients[3]
    c <- sum.fiveplc()$coefficients[4]
    m <- sum.fiveplc()$coefficients[5]
    
    dt()$NewConcentration <- concentration(dt()$Mean_Absorbance)
    
    dt()
  
  })

}

shinyApp(ui, server)

I keep getting errors, in this example, the error is Error: object 'd' not found. I have tried all kinds of different formats, including putting the function within the server as a reactive, but then I got the error Error: unused argument (dt()$Mean_Absorbance).

Can anyone sense what is going on wrong here?

Any help would be vastly appreciated. Thank you so much.


Solution

  • Functions can look up values from their lexical scope (where they were defined), but not from their dynamic scope (environment where they are called).

    You should define the coefficients as parameters to your function:

    concentration <- function(y, a, b, c, d, m) {
      c * (((a - d) / (y - d)) ^ (1 / m) - 1) ^ (1 / b)
    }
    

    Then pass the fitted coefficients as arguments like so:

    b <- sum.fiveplc()$coefficients[1] # store model coefficients to variables
    d <- sum.fiveplc()$coefficients[2]
    a <- sum.fiveplc()$coefficients[3]
    c <- sum.fiveplc()$coefficients[4]
    m <- sum.fiveplc()$coefficients[5]
    
    result <- dt()
    result$NewConcentration <- concentration(dt()$Mean_Absorbance, a, b, c, d, m)
    result