Using ar() in brms for structured priors - any examples?

I’m trying to make use of some of the suggestions from Gelman and colleagues in their paper ‘Improving multilevel regression and poststratification with structured priors’. The idea is that autocorrelation structures can be used to - for example - ensure that shrinkage does not just apply equally across all age categories. Instead, age 65+ would borrow more information from 45-64 than from 18-24, and so on.

I can’t work out how to implement this in brms at all - I’ve tried all sorts of things using the ar() function in brms which has been suggested by both a colleague and keeps being suggested by LLMs, but I can’t get any kind of syntax that works.

In the simple example to work with is the following setup:

test_data <-
  tibble::tibble(age = sample(c("18-24", "25-44", "45-64", "65+"), size = 500, replace = TRUE),
                 region = sample(c("Dessert", "Jungle", "Grassland"), size = 500, replace = TRUE),
                 sex = sample(c(-.5, .5), size = 500, replace = TRUE),
                 outcome = sample(c(0, 1), size = 500, replace = TRUE)) %>% 
  dplyr::mutate(age = factor(age, levels = c("18-24", "25-44", "45-64", "65+"),
                             ordered = TRUE),
                age_order = as.numeric(age))

test_form <-
  outcome ~ sex + (1 | region) + (1 | age)

test_fit <-
  brms::brm(formula = test_form,
            family = bernoulli(),
            data = test_data)

But how, rather than age being specified just as a general verying intercept (1 | age) can I make it so that there is some kind of autocorrelation, with levels borrowing more from closer categories?

{brms} can handle complex penalized spline structures from the {mgcv} package, including the mrf basis. This basis allows you to supply structured precision matrices for the penalties, one of which could be a random walk precision. The {mrftools} package, currently only on GitHub, helps to set up these penalties. Here is a reprex to illustrate how a factor variable can be used to set up a random walk basis, which will help achieve what you are after: Using RW penalties in {mvgam} · Issue #10 · eric-pedersen/MRFtools · GitHub

Thanks Nicholas - I didn’t yet have the time to test this out but it looks promising.

1 Like

Hi Nicholas - based on the example in that issue I don’t quite see how to utilise this in brms. The constructed penalty gets fed to the gam() function in the argument xt, but I don’t see anything equivalent in brms. If you can suggest how this would be utilised it would be much appreciated.

I’m also interested to know still if there is something fundamentally wrong with the attempt to use ar() function for this as some others have suggested to me, of it is just harder to implement than I thought it should be.

You feed xt to the s() function, which is supported by {brms}. If you try that exact example but use brm() instead of gam() it should work. But unfortunately I’m away from the computer so can’t test right now. I’ll have to leave it to someone else to comment on the functionality of ar() though

Thanks for the quick response - I’ll try this out.

1 Like