Say I have some fat-tail Student-t data, I fit a model using the Student-t likelihood, and the bulk of the \nu posterior is sitting well in the low-end of the range. Further say I want to use those posterior draws from \nu and \sigma to compute a posterior for the standard deviation using the formula \sigma \sqrt{\nu / (\nu - 2)} for \nu > 2 (see Student's t-distribution - Wikipedia). For the prior on \nu, I use the brms default \operatorname{gamma}(2, 0.1), but in two different ways:
- set the lower bound at 2 (
lb = 2
), or - set the lower bound at 0, but just drop all posterior draws for which \nu \le 2.
Are both methods equally valid?
Here’s a quick example. I’m using brms here, but this is a more general Stan question.
# Load packages
library(tidyverse)
library(brms)
library(ggdist)
# Simulate fat-tail t data
set.seed(1)
student_data <- data.frame(y = rstudent_t(n = 100, df = 3))
# Fit the two versions of the model
fit_nu_lb_0 <- brm(
data = student_data,
family = student,
y ~ 1,
prior = prior(gamma(2, 0.1), class = nu, lb = 0),
cores = 1, seed = 1, warmup = 500, iter = 5500)
fit_nu_lb_2 <- brm(
data = student_data,
family = student,
y ~ 1,
# Notice the different `lb` setting
prior = prior(gamma(2, 0.1), class = nu, lb = 2),
cores = 1, seed = 1, warmup = 500, iter = 5500)
# Extract and save the posterior draws
draws_lb_0 <- as_draws_df(fit_nu_lb_0)
draws_lb_2 <- as_draws_df(fit_nu_lb_2)
For the model that used lb = 0
, there were indeed several draws for \nu below the 2 threshold.
draws_lb_0 |>
summarize(n_below_2_lb = sum(nu <= 2),
percent_below_2_lb = 100 * mean(nu <= 2))
draws_lb_0 |>
ggplot(aes(x = nu, fill = nu > 2, group = nu > 2)) +
geom_histogram(breaks = seq(from = 0, to = 60, by = 0.1)) +
scale_x_continuous(expression(nu), breaks = 0:5 * 2) +
scale_y_continuous(NULL, breaks = NULL) +
coord_cartesian(xlim = c(0, 10))
# A tibble: 1 Ă— 2
n_below_2_lb percent_below_2_lb
<int> <dbl>
1 617 3.08
However, if one dropped all those draws for which \nu \le 2 from the fit_lb_0
version of the model, the posteriors for \nu look very similar for both versions of the model.
bind_rows(draws_lb_0, draws_lb_2) |>
mutate(lb = rep(c("lb = 0", "lb = 2"), each = n() / 2)) |>
# Drop those unwanted draws
filter(nu > 2) |>
ggplot(aes(x = nu, y = lb)) +
stat_histinterval(breaks = seq(from = 0, to = 60, by = 0.1)) +
scale_x_continuous(expression(nu), breaks = 0:5 * 2) +
scale_y_discrete(NULL, expand = expansion(mult = 0.1)) +
coord_cartesian(xlim = c(1, 10))
Is this valid?