Extracting default knot locations from fitted brms models

How can one extract default knot locations that were assigned during model fitting in brms?

Using the example provided here but without custom knot locations, i.e.

set.seed(2)
dat <- mgcv::gamSim(1,n=400,dist="normal",scale=2)

library(brms)
b2 <- brm(y~s(x2, k = 3), data=dat)
summary(b2)

how can we extract the locations of the knots that were used for the spline s(x2, k = 3) from the object b2?

brms is by default using mgcv regularized thin plate splines which uses all the data point locations as knots (so that is the answer to your question), but then forms a basis function approximation and argument k is for the number of basis functions. See more in R: Defining smooths in GAM formulae.

1 Like

And the paper explaining regularized thin plate regression splines https://doi.org/10.1111/1467-9868.00374

1 Like

I am sorry, the example I have used above might be too reduced. I am looking for a solution in a more general sense, meaning for different types of smoothers, such as P-splines.

Or in other words:
In mgcv I can do the following:

> g2 <- gam(y~s(x2, bs = "ps"), data=dat)
> g2$smooth[[1]]$knots
 [1] -0.425024835 -0.282599481 -0.140174127  0.002251227  0.144676581  
 [6]  0.287101935  0.429527289  0.571952642  0.714377996  0.856803350  
[11]  0.999228704  1.141654058  1.284079412  1.426504766

Is there any way to do the same with a brmsfit-object resulting from e.g. b2 <- brm(y~s(x2, bs = "ps"), data=dat)?

I guess for such simple models, the knots used by mgcv and brms might be the same, and one can just get the knots from the corresponding mgcv-model. But when it comes to more complex models with nlf()-formulas, there is no straightforward way to construct such models with mgcv.

Hey @niklas191 , when fitting the model without nonlinear syntax, I found the knots inside the basis list like so:

b2 <- brm(y ~ s(x2, bs = 'ps'), data = dat, cores = 4, backend = 'cmdstanr')
b2$basis |> str()
b2$basis$dpars$mu$sm$`s(x2, bs = "ps")`$sm[[1]]$knots
[1] -0.425024835 -0.282599481 -0.140174127  0.002251227  0.144676581  0.287101935  0.429527289  0.571952642  0.714377996
[10]  0.856803350  0.999228704  1.141654058  1.284079412  1.426504766

For nonlinear syntax (nlf), it’s similar except they’re not inside dpars but rather inside nlpars:

b2_nlf <- bf(y ~ a, a ~ s(x2, bs = 'ps'),nl = T)
b2_nl <- brm(b2_nlf, data = dat, cores = 4, backend = 'cmdstanr')
summary(b2_nl) #same

b2_nl$basis$dpars %>% str() # just mu
b2_nl$basis$dpars$mu # empty

b2_nl$basis$nlpars %>% str() # there it is
b2_nl$basis$nlpars$a$sm$`s(x2, bs = "ps")`$sm[[1]]$knots
 [1] -0.425024835 -0.282599481 -0.140174127  0.002251227  0.144676581  0.287101935  0.429527289  0.571952642  0.714377996
[10]  0.856803350  0.999228704  1.141654058  1.284079412  1.426504766

I think @avehtari is right that the knots for either mgcv or brms should be identical (presumably just depends on the values of the predictor variable(s) no matter if it’s nonlinear syntax or not?) but I’m not sure how to find that out specifically.

1 Like