g’day @gowerc ,
Not a direct answer to your question but a couple thoughts/ideas:
First, if f(X_i) is simple enough to work with, perhaps use the same formula for both \mu_i and for \sigma_i ? So I’m replacing \mu_i \cdot \sigma with \sigma_i and using \sigma_i = f(X_i) as well.
I think this is close but not exactly what you had in mind, since distributional models in brms
will fit separate parameters for both mu
and sigma
:
Code example with distributional regression
library(brms); library(tidyverse)
set.seed(20240807)
dat <- tibble(
x = seq(1, 30, length.out = 100)
) %>%
mutate(
mu_y = 10 + 3 * x,
sigma_y = mu_y * 0.15, # so base sigma is 0.15, but scales with mu
# i.e., sigma_y = 0.15 * (10 + 3 x) = 1.5 + 0.45 x
y = rnorm(n = n(), mean = mu_y, sd = sigma_y)
# today i learned you can give rnorm vector means and sd's
)
dat %>% ggplot(aes(x, y))+geom_point() # cool
# first test with a distributional model, but not quite what OP was thinking
# sigma just depends on x (not on mu)
# but if mu and sigma use same formula... that's kinda it, no?
bf_1 <- bf(
y ~ x,
sigma ~ x,
family = gaussian()
)
# NOTE: brms automatically applies the log link to sigma (zero-positive)
get_prior(bf_1, dat)
fit_1 <- brm(bf_1, data = dat, backend = 'cmdstanr', cores = 4, chains = 4)
summary(fit_1); pp_check(fit_1) # not bad
(Is there a way to get parameters on both mu
and sigma
to correlate in brms
?)
My example cheats by using a linear case. Also, sigma
is fit on the log-link scale, so its parameters are multiplicative instead of additive like in my simulated data.
Another thought I had when I first read your question was to just use a distribution whose variance scales with the mean. The Gamma is a natural contender if it suits your data (no zeroes, positive only?), since var(Y)=\mu^2\phi where \phi is the dispersion parameter. But that’s the square of the mean it scales with…
This model does ok in my example but doesn’t outperform fit_1
.
Example with Gamma
bf_2 <- bf(
y ~ x, family = Gamma('log')
)
get_prior(bf_2, dat)
fit_2 <- brm(bf_2, data = dat, backend = 'cmdstanr', cores = 4, chains = 4)
summary(fit_2); pp_check(fit_2) # a wee bit off compared to fit_1
# compare models:
fit_1 <- add_criterion(fit_1, 'loo')
fit_2 <- add_criterion(fit_2, 'loo')
loo_compare(fit_1, fit_2) # favors fit_1
I did try digging around if you can get brms
to use mu * sigma
somehow but didn’t find anything. Maybe this requires editing Stan code? Curious to hear what others know here :^)
hope that helps