How can I define/compute named objects and input them into brms functions? (For literate coding)

,

I want to define a numerical argument to a brms function that gets plugged into a brm model fitting. I want to define it first and then plug in the name of this thing into the function. But it seems to be keeping it as a string/name and not the number itself:

First computing reasonable priors in a literate and transparent way, which I discuss in the Quarto:

prior_slope_click_ub <- 0.25

se_prior_slope_click <- (logit(prior_slope_click_ub) - prior_intercept_click)/1.96

Next I put these arguments into a function …

prior_click <- c(prior(normal(-prior_intercept_click, se_prior_click), class = Intercept), 
+     prior(normal(0, se_prior_slope_click), class = b))

prior_click

But it uses the names and not the values:

prior     class coef group resp dpar nlpar   lb   ub source
 normal(-prior_intercept_click, se_prior_click) Intercept                            <NA> <NA>   user
normal(0, se_prior_slope_click)         b                            <NA> <NA>   user

This is important to my ‘define everything and show the process’ workflow, which I think relates to ‘literate programming’.

It looks like you could build your priors using prior_string(), as in the example below. Is that what you had in mind?

library(brms)

# Make priors
mp = function(mean, sd, ...) {
  prior_string(paste0("normal(",mean,",",sd,")"), class="b", ...)
}

priors = c(mp(0, 3),
           mp(0, 1, coef="var3"))

priors
#>        prior class coef group resp dpar nlpar   lb   ub source
#>  normal(0,3)     b                            <NA> <NA>   user
#>  normal(0,1)     b var3                       <NA> <NA>   user
# Adapting the OP example
prior_slope_click_ub <- 0.25
prior_intercept_click = -2

se_prior_slope_click <- (logit_scaled(prior_slope_click_ub) - prior_intercept_click)/1.96

priors = mp(-prior_intercept_click, se_prior_slope_click)

priors
#> b ~ normal(2,0.459891689455046)

Then, within brm you can set the argument prior=priors to pass your priors into the model fitting process.

1 Like

Yes, this works, thanks! For reference, here is how I adapt it in my context, e…g., renaming it make_prior_normal: so it’s clear what it is doing

#helper function to 'make priors'
make_prior_normal <- function(mean, sd, ...) {
  prior_string(paste0("normal(", mean, ",", sd, ")"), ...)
}

prior_click_per_reach <- 0.028

prior_intercept_click <-  logit(prior_click_per_reach)

prior_intercept_click_ub <- 0.15
se_prior_click <- (logit(prior_intercept_click_ub) - prior_intercept_click)/1.96

prior_slope_click_ub <- 0.25
se_prior_slope_click <- (logit(prior_slope_click_ub) - prior_intercept_click)/1.96

prior_click <- c(
  make_prior_normal(prior_intercept_click, se_prior_click, class = "Intercept"),
make_prior_normal(0, se_prior_slope_click, class = "b")
)
> prior_click
                                       prior     class coef
 normal(-3.54715129428524,0.924770530049556) Intercept     
                  normal(0,1.24925459470262)         b     
 group resp dpar nlpar   lb   ub source
                       <NA> <NA>   user
                       <NA> <NA>   user