I have a biomarker data that has lots of below limit of quantifications value. To understand/interpret this data into % inhibition of this biomarker - if i treat the values below limit of quantification as 0 then it will give me 100% inhibition; if i treat the values as lloq - it will give me inhibition of 80% ; if i treat the values as lloq as half of lloq then it will give me inhibition of 90%.
How can i use brm into evaluating this data using nlm.
Question 2: Currently i am using rstanemax package into fitting this emax model. And use the fitting manually (i.e run with scenario 1 - assuming lloq values as 0, then scenario 2 - assuming lloq as lloq, scenario 3 - assuming lloq as half of lloq).
Can anyone help me to understand :
Can my data be used in brm and what exactly can brm do here
What is the difference between rtanemax and brms - what are the gap
I can’t speak to rstanemax, but what you’re describing sounds like a challenge with left-censored data (informative missingness: you only know it’s below a LOQ). You could try using the cens() operator in brms to set this up (read up at ?brms::cens), after creating a new column that indicates when data are left-censored or fully observed.
The beauty with a model that accounts for censoring is that you probably won’t need to consider those 3 scenarios you described (don’t make any further assumption than you need).
Try out some test data here – you’ll be impressed! (To me, getting a model to work in spite of missing data is a real pleasure, and it’s all the better with what brms handles for you.)
Here’s a simple example:
library(brms)
library(tidyverse)
set.seed(20240419)
dat <- tibble(x = 1:30) %>%
mutate(y_true = 5 + 1 * x + rnorm(n= n(), 0, sd = 7),
# indicate which values are left-censored or not
y_cens = if_else(y_true < 10, 'left', 'none'),
# set values below detection to detection limit
y_obs = if_else(y_true < 10, 10, y_true)
)
dat %>%
ggplot(aes(x, y_obs, color = y_cens))+geom_point()
# model with left-censoring
fit <- brm(y_obs | cens(y_cens) ~ x,
family = gaussian(),
data = dat,
cores = 4, chains = 4)
summary(fit)
conditional_effects(fit)
# examine the Stan code:
brms::stancode(fit)
# note especially this bit for the likelihood:
# for (n in 1:N) {
# // special treatment of censored data
# if (cens[n] == 0) {
# target += normal_lpdf(Y[n] | mu[n], sigma);
# } else if (cens[n] == 1) {
# target += normal_lccdf(Y[n] | mu[n], sigma);
# } else if (cens[n] == -1) {
# target += normal_lcdf(Y[n] | mu[n], sigma);
# }
# }
## contrast with more naive model
# assume data is at limit
dat %>% lm(y_obs ~ x, data = .) %>% summary()
# both parameters are biased