2

I'd like to create a set of parameters for use in a brms model in R:

library(brms)

tmp <- prior(normal(10,2), nlpar = "x")

Ideally I'd like to extract the values for each prior (e.g. normal(10,2)) from an imported matrix, for example:

priors <- cbind(c(10,20,30,40), c(2,4,6,8))

i <- 1
tmp <- prior(normal(priors[i,1], priors[i,2]), nlpar = "x")

However, this gives the following output:

#b_x ~ normal(priors[i, 1], priors[i, 2])

instead of the numeric values:

#b_x ~ normal(10, 2)

I realize this is probably pretty basic, but I can't figure out the correct way to do this. I've tried:

prior(normal(as.numeric(priors[i,1]), as.numeric(priors[i,2])), nlpar = "x")
prior(normal(as.list(priors[i,1]), as.list(priors[i,2])), nlpar = "x")
prior(normal(paste(priors[i,1]), paste(priors[i,2])), nlpar = "x")
prior(normal(get(priors[i,1]), paste(get[i,2])), nlpar = "x")

Can someone show me where I'm going wrong here? extracting by position [,] seems to work for other functions, e.g., lm(priors[,1]~priors[,2]).

Zheyuan Li
  • 71,365
  • 17
  • 180
  • 248

2 Answers2

3

Another way to do this is with the function brms::stanvar(). Take a look at its man page here. This is advantageous because you can change the prior within stanvar() and refit the model without having to recompile it. Because brms is a wrapper for Stan, this is the equivalent of passing hyperparameters of a prior distribution as part of the data block in a Stan model.

Each call to stanvar() takes two arguments, the value and a string which is the name of the variable that can be used later on inside prior(). Then combine each individual variable together using + and pass that to the stanvars argument of brm().

For your example, you could do like so:

prior_params <- stanvar(priors[i, 1], 'prior_mean') + stanvar(priors[i, 2], 'prior_sd')

x_prior <- prior(normal(prior_mean, prior_sd), nlpar = 'x')

brm(..., prior = x_prior, stanvars = prior_params)
qdread
  • 3,389
  • 19
  • 36
  • Curious why this was unaccepted, as it is the recommended way of passing prior hyperparameters programmatically to a brm model – qdread Jan 03 '23 at 13:59
2

Basically, you want to evaluate priors[i, 1] and priors[i, 2] when they are passed to prior().

priors <- cbind(c(10, 20, 30, 40), c(2, 4, 6, 8))

i <- 1

## use `do.call()`
do.call("prior",
        list(prior = call("normal", priors[i, 1], priors[i, 2]),
             nlpar = "x"))
#b_x ~ normal(10, 2)

## use `eval(call())`
eval(call("prior", call("normal", priors[i, 1], priors[i, 2]), nlpar = "x"))
#b_x ~ normal(10, 2)

While this works, as I read ?prior, I find that it is recommended to specify distribution as a string. Therefore, the following also works.

## I used %d because values in `priors` matrix are integers
## in general, it is safer to use %f for real numbers
eval(call("prior",
          sprintf("normal(%d, %d)", priors[i, 1], priors[i, 2]),
          nlpar = "x"))
#b_x ~ normal(10, 2)

Note:

I am also a newbie with brms, so I honestly think the other answer more native/natural to the package. (This is the benefit of learning by answering questions; I always get useful feedback from peers.)

As said, it is the recommended way, because it is the brms equivalent of passing prior hyperparameters as data to a Stan model.

Zheyuan Li
  • 71,365
  • 17
  • 180
  • 248
  • 1
    On the other hand, this is a more general solution that will work in other situations besides brms. – qdread Jul 01 '22 at 12:54