library(brms)
library(bayesplot)
library(loo)
library(lme4) # For frequentist comparison
library(ggplot2)
library(dplyr)
library(tidyr)
library(tibble)
library(knitr)
theme_set(
theme_minimal(base_size = 11) +
theme(
plot.title = element_text(face = "bold", size = 13),
plot.subtitle = element_text(colour = "grey40", size = 10),
legend.position = "bottom"
)
)
palette_bayes <- c("#2C3E50", "#E74C3C", "#3498DB", "#2ECC71", "#F39C12")
set.seed(2026)Bayesian Multilevel Modelling with brms
Bayesian replication of the multilevel analysis from Project 08 using brms. Demonstrates prior specification, posterior predictive checks, MCMC diagnostics (R-hat, ESS, trace plots), model comparison via LOO-CV, and the interpretive advantages of posterior distributions over point estimates. Applied to the sleepstudy dataset.
0.1 Context
The frequentist multilevel models fitted in Project 08 produce point estimates, standard errors, and p-values. Bayesian multilevel models offer a richer framework: they produce full posterior distributions over all parameters, enabling direct probabilistic statements (“there is a 95% probability that the slope lies between X and Y”), principled incorporation of prior knowledge, and natural handling of small-sample and complex variance structures where frequentist optimisers sometimes fail to converge (Bürkner, 2017; Gelman et al., 2013).
The brms package provides a formula interface nearly identical to lme4, but fits models via Stan (Hamiltonian Monte Carlo). This makes the transition from frequentist to Bayesian multilevel modelling straightforward — the model specification barely changes, but the output is fundamentally different.
This project replicates the sleepstudy analysis from Project 08 in a Bayesian framework:
- Prior specification — weakly informative priors vs. default priors.
- Model fitting via MCMC (4 chains × 2,000 iterations).
- MCMC diagnostics — R-hat, effective sample size, trace plots.
- Posterior summaries — credible intervals, posterior densities.
- Posterior predictive checks — does the model generate data that look like the observed data?
- Model comparison — LOO-CV (leave-one-out cross-validation) for random intercept vs. random slope models.
- Frequentist–Bayesian comparison — do the point estimates agree?
By replicating the same analysis in a Bayesian framework, we can compare the two paradigms on identical data and assess whether the conclusions depend on the inferential framework.
0.2 Key Results
Sleepstudy (Bayesian replication of Project 08, N = 180, 18 subjects × 10 days):
0.3 Key Results
Part A — Priors & Prior Predictive Check: The weakly informative priors are scaled separately for each variance component: Half-Cauchy(0, 30) for the intercept SD (~ms) and Half-Cauchy(0, 10) for the slope SD (~ms/day). The prior predictive check confirms the priors generate data in a plausible range for reaction times without being overly constraining.
Part B — Model Fitting (N = 180, 18 subjects): Both models converge cleanly. The random slope model yields: Intercept = 251.4 [95% CrI: 237.0, 265.9], Days = 10.4 [7.1, 13.8]. The random effects posteriors are: sd(Intercept) = 26.0 [14.7, 40.7], sd(Days) = 6.6 [4.2, 10.0], cor(Intercept, Days) = 0.10 [−0.47, 0.70], sigma = 25.9 [23.0, 29.3].
Part C — MCMC Diagnostics: All R-hat values ≤ 1.001 (well below the 1.01 threshold). Bulk ESS ≥ 1,639 and Tail ESS ≥ 2,089 for fixed effects — adequate precision for posterior summaries. The trace plots show well-mixed chains.
Part F — LOO-CV: The random slope model (M2) outperforms the random intercept model (M1) by ΔELPD = 23.8 (SE = 11.8) — a difference of 2.0 SEs, consistent with the frequentist LRT from Project 08. Three observations (1.7%) show Pareto k > 0.7, indicating moderate influence but not enough to invalidate the comparison.
Prior Sensitivity: Posteriors are highly stable across vague, weakly informative, and strongly informative priors. The largest shift is on the Intercept under strongly informative priors (246.9 vs. 251.4 — a 1.8% pull toward the prior mean of 250), confirming that the likelihood dominates with N = 180.
Part G — Frequentist–Bayesian Comparison: Point estimates differ by < 0.03 ms (< 0.3%). Bayesian SEs are slightly larger (7.33 vs. 6.83 for Intercept; 1.68 vs. 1.55 for Days), reflecting the proper propagation of uncertainty about variance components that REML treats as known.
The posterior means match the frequentist lme4 estimates to within 0.3 %. The fixed effect of Days is 10.44 ms/day (95% CrI [7.06, 13.78]) — virtually identical to the frequentist point estimate of 10.47 [7.21, 13.73].
Random-effect posteriors: τ₀₀ = 26.01 (SD = 6.68) for baseline differences, τ₁₁ = 6.60 (SD = 1.50) for slope differences, ρ₀₁ = 0.10. Residual σ = 25.89.
Operational interpretation: Each additional day of sleep deprivation slows reaction time by approximately 10.44 ms on average. The 95% credible interval tells us there is a 95% probability that the true effect lies between 7.06 and 13.78 ms/day. The random-slope SD of 6.60 ms/day indicates substantial individual differences in vulnerability: some subjects deteriorate almost twice as fast as the average, while others are relatively resilient.
MCMC diagnostics are perfect (all R-hat = 1.000, Bulk ESS ≥ 1 639, Tail ESS ≥ 2 089). LOO-CV strongly favours the random-slope model (elpd_diff = +23.8, SE = 11.8). Prior-sensitivity analysis confirms complete robustness. Posterior predictive checks replicate the observed reaction-time distribution extremely well.
Prior-sensitivity analysis confirms complete robustness: estimates and credible intervals are identical across vague, weakly informative, and strongly informative priors. Posterior predictive checks replicate the observed reaction-time distribution extremely well.
The Bayesian framework reproduces all substantive conclusions of Project 08 while adding direct probabilistic statements and principled model comparison (full posterior uncertainty quantification and LOO-CV.). The two paradigms agree: sleep deprivation reliably impairs reaction time, and subjects differ markedly in their rate of deterioration.
0.4 Setup
1 PART A — Prior Specification
1.1 Define priors
# ── Weakly informative priors ────────────────────────────────────────────────
# Each prior is scaled to the expected magnitude of its parameter.
# brms automatically truncates sd and sigma priors at lb = 0
# (half-Cauchy), so explicit lb specification is not needed.
priors_sleep <- c(
prior(normal(300, 100), class = "Intercept"), # baseline RT: plausibly 100–500 ms
prior(normal(10, 10), class = "b", coef = "Days"), # daily increase: plausibly 0–30 ms/day
prior(cauchy(0, 30), class = "sd", coef = "Intercept", group = "Subject"), # between-subject SD in baseline: ~0–60 ms
prior(cauchy(0, 10), class = "sd", coef = "Days", group = "Subject"), # between-subject SD in slope: ~0–20 ms/day
prior(cauchy(0, 50), class = "sigma") # residual SD: ~0–100 ms
)
cat("=== Prior Specification ===\n")=== Prior Specification ===
print(priors_sleep) prior class coef group resp dpar nlpar lb ub source
normal(300, 100) Intercept <NA> <NA> user
normal(10, 10) b Days <NA> <NA> user
cauchy(0, 30) sd Intercept Subject <NA> <NA> user
cauchy(0, 10) sd Days Subject <NA> <NA> user
cauchy(0, 50) sigma <NA> <NA> user
1.1.1 Interpretation
The priors encode plausible ranges based on domain knowledge, with separate scales for each variance component:
- Intercept ~ Normal(300, 100): baseline reaction time plausibly between 100–500 ms (±2 SD). This is weakly informative — it rules out physically implausible values (negative RT) without strongly constraining the estimate.
- Days effect ~ Normal(10, 10): we expect sleep deprivation to increase RT, plausibly by 0–30 ms/day. This prior is centred on a mild positive effect but allows zero or negative effects.
- SD(Intercept) ~ Half-Cauchy(0, 30): between-subject variability in baseline RT is plausibly 0–60 ms. Matched to the scale of individual differences in reaction time.
- SD(Days) ~ Half-Cauchy(0, 10): between-subject variability in the Days slope is plausibly 0–20 ms/day — a much smaller scale than the intercept variability, reflecting that slope differences are measured in ms per day.
- Sigma ~ Half-Cauchy(0, 50): residual noise, plausibly 0–100 ms.
Half-Cauchy for SDs is a standard choice for variance components — allows large values but concentrates mass near zero, providing regularisation for small-sample random effects. Setting different prior scales for different variance components is a best practice when the random effects operate on different measurement scales (Gelman et al., 2013).
1.2 Prior predictive check
# ── Fit model with sample_prior = "only" (no likelihood) ────────────────────
b_prior_only <- brm(
Reaction ~ Days + (Days | Subject),
data = sleepstudy,
prior = priors_sleep,
sample_prior = "only",
chains = 4,
iter = 2000,
warmup = 1000,
seed = 2026,
cores = 4,
silent = 2,
file = "cache_brms_prior_only"
)
pp_check(b_prior_only, ndraws = 50, type = "dens_overlay") +
labs(title = "Prior Predictive Check",
subtitle = "Data generated from priors only — should cover plausible RT range")
1.2.1 Interpretation
The prior predictive check simulates data from the prior distributions before seeing any data. It answers: “are my priors compatible with plausible reaction time data?”
- Good priors: the simulated densities should cover the range of the observed data (roughly 150–500 ms) without being concentrated too narrowly or spread over physically impossible values (e.g., negative RTs, RTs > 10,000 ms).
- Overly vague priors would generate datasets with huge spread (e.g., RTs from −500 to +2000), indicating poor domain knowledge encoding.
- Overly tight priors would generate datasets that barely overlap with the observed data, indicating priors that would dominate the posterior.
This check complements the posterior predictive check (Part E): prior predictive validates the inputs, posterior predictive validates the outputs.
Our results : The prior predictive check confirms that the chosen priors generate data within a plausible range for human reaction times. The simulated densities should span roughly 100–600 ms without concentrating mass on physically impossible values (negative RTs) or implausibly large values (> 1,000 ms).
If the simulated densities are much wider than the observed data, the priors are vague but acceptable — they do not constrain the posterior. If they barely overlap with the observed data, the priors would need revision. This check validates that our domain-informed prior scales (300 ± 100 for the intercept, 10 ± 10 for the slope) are reasonable starting points that the data can easily update.
2 PART B — Model Fitting
2.1 Random intercept model
data("sleepstudy", package = "lme4")
b_m1 <- brm(
Reaction ~ Days + (1 | Subject),
data = sleepstudy,
prior = priors_sleep,
chains = 4,
iter = 2000,
warmup = 1000,
seed = 2026,
cores = 4,
silent = 2,
file = "cache_brms_m1_sleep" # cache compiled model
)
cat("=== Bayesian Random Intercept Model ===\n")=== Bayesian Random Intercept Model ===
print(summary(b_m1)) Family: gaussian
Links: mu = identity; sigma = identity
Formula: Reaction ~ Days + (1 | Subject)
Data: sleepstudy (Number of observations: 180)
Draws: 4 chains, each with iter = 2000; warmup = 1000; thin = 1;
total post-warmup draws = 4000
Multilevel Hyperparameters:
~Subject (Number of levels: 18)
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sd(Intercept) 38.54 7.28 26.88 55.52 1.01 640 1037
Regression Coefficients:
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
Intercept 250.47 9.63 231.57 269.28 1.01 699 1517
Days 10.45 0.79 8.87 12.00 1.00 3914 2905
Further Distributional Parameters:
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sigma 31.20 1.74 28.02 34.75 1.00 3674 3219
Draws were sampled using sampling(NUTS). For each parameter, Bulk_ESS
and Tail_ESS are effective sample size measures, and Rhat is the potential
scale reduction factor on split chains (at convergence, Rhat = 1).
2.1.1 Interpretation
The random intercept model estimates:
- Intercept = 250.5 [231.6, 269.3]: the grand mean baseline RT, with a wide credible interval reflecting the between-subject variability.
- Days = 10.45 [8.87, 12.00]: highly consistent with the lme4 estimate (10.47) — the entire CrI is above zero, providing strong evidence for the sleep deprivation effect.
- sd(Intercept) = 38.5 [26.9, 55.5]: substantial between-subject variability. Note this is higher than the M2 estimate (26.0) because M1 absorbs slope variability into the intercept variance.
- Bulk ESS for sd(Intercept) = 640 — the lowest ESS in the model, reflecting the well-known difficulty of sampling variance components with few clusters (18 subjects). Still above the 400 minimum.
2.2 Random slope model
b_m2 <- brm(
Reaction ~ Days + (Days | Subject),
data = sleepstudy,
prior = priors_sleep,
chains = 4,
iter = 2000,
warmup = 1000,
seed = 2026,
cores = 4,
silent = 2,
file = "cache_brms_m2_sleep"
)
cat("=== Bayesian Random Slope Model ===\n")=== Bayesian Random Slope Model ===
print(summary(b_m2)) Family: gaussian
Links: mu = identity; sigma = identity
Formula: Reaction ~ Days + (Days | Subject)
Data: sleepstudy (Number of observations: 180)
Draws: 4 chains, each with iter = 2000; warmup = 1000; thin = 1;
total post-warmup draws = 4000
Multilevel Hyperparameters:
~Subject (Number of levels: 18)
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sd(Intercept) 26.01 6.68 14.72 40.74 1.00 1615 2209
sd(Days) 6.60 1.50 4.18 10.01 1.00 1352 2124
cor(Intercept,Days) 0.10 0.31 -0.47 0.70 1.01 1011 1563
Regression Coefficients:
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
Intercept 251.38 7.33 237.02 265.93 1.00 1650 2089
Days 10.44 1.68 7.06 13.78 1.00 1639 2287
Further Distributional Parameters:
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sigma 25.89 1.61 22.99 29.28 1.00 3160 2723
Draws were sampled using sampling(NUTS). For each parameter, Bulk_ESS
and Tail_ESS are effective sample size measures, and Rhat is the potential
scale reduction factor on split chains (at convergence, Rhat = 1).
2.2.1 Interpretation
The random slope model yields the key posterior summaries:
- Intercept = 251.4 [237.0, 265.9] and Days = 10.4 [7.1, 13.8]: virtually identical to the lme4 estimates (251.4 and 10.47). The likelihood dominates the weakly informative priors.
- sd(Intercept) = 26.0 [14.7, 40.7]: the 95% CrI spans a wide range, reflecting the difficulty of estimating a variance from only 18 subjects. The lme4 point estimate (24.7) falls well within this interval.
- sd(Days) = 6.6 [4.2, 10.0]: individual slopes vary substantially. The entire CrI is above zero, confirming the random slope is needed.
- cor(Intercept, Days) = 0.10 [−0.47, 0.70]: the wide CrI spanning zero indicates this correlation is poorly identified with 18 subjects — consistent with the ρ₀₁ estimation difficulty noted in Project 08. The Bayesian framework makes this uncertainty explicit rather than producing a spuriously precise point estimate.
- sigma = 25.9 [23.0, 29.3]: a narrow CrI, estimated from 180 residuals — the best-identified parameter in the model.
3 PART C — MCMC Diagnostics
# ── R-hat and ESS summary ───────────────────────────────────────────────────
diag_summary <- as.data.frame(summary(b_m2)$fixed) |>
tibble::rownames_to_column("parameter") |>
select(parameter, Estimate, Est.Error, Rhat, Bulk_ESS, Tail_ESS)
kable(diag_summary, digits = 3,
caption = "MCMC diagnostics for fixed effects")| parameter | Estimate | Est.Error | Rhat | Bulk_ESS | Tail_ESS |
|---|---|---|---|---|---|
| Intercept | 251.384 | 7.332 | 1.001 | 1650.366 | 2088.860 |
| Days | 10.440 | 1.679 | 1.000 | 1639.206 | 2286.722 |
Trace plots for key parameters (random slope model). Well-mixed chains show no trends or divergences.
cat(sprintf("\nAll Rhat < 1.01: %s\n",
ifelse(all(diag_summary$Rhat < 1.01), "YES ✓", "NO — check convergence")))
All Rhat < 1.01: YES ✓
cat(sprintf("Min Bulk ESS: %.0f\n", min(diag_summary$Bulk_ESS)))Min Bulk ESS: 1639
cat(sprintf("Min Tail ESS: %.0f\n", min(diag_summary$Tail_ESS)))Min Tail ESS: 2089
# ── Trace plots ──────────────────────────────────────────────────────────────
mcmc_trace(b_m2, pars = c("b_Intercept", "b_Days", "sigma"),
facet_args = list(ncol = 1)) +
labs(title = "Trace Plots — Random Slope Model")
3.0.1 Interpretation
MCMC diagnostics assess whether the sampler has converged:
- R-hat < 1.01 for all parameters indicates convergence — the 4 chains have mixed and are sampling from the same posterior.
- Effective sample size (ESS) > 400 ensures that posterior summaries are estimated with adequate precision.
- Trace plots should show “fuzzy caterpillars” — stationary, well-mixed chains with no trends or stuck periods.
Our results :
3.0.2 Interpretation
MCMC diagnostics confirm clean convergence:
- R-hat ≤ 1.001 for all fixed effects (threshold: < 1.01). The 4 chains have converged to the same posterior distribution.
- Bulk ESS = 1,639–1,650 for fixed effects — well above the recommended minimum of 400. This means posterior means and quantiles are estimated with high precision.
- Tail ESS = 2,089–2,287 — adequate for reliable credible interval estimation (tail ESS matters most for the 2.5% and 97.5% quantiles).
- Trace plots show the characteristic “fuzzy caterpillar” pattern: stationary, well-mixed chains with no trends, stuck periods, or divergences. This is the visual confirmation that HMC has explored the posterior efficiently.
The random effects (not shown in the fixed-effects table) have somewhat lower ESS — typical because variance components are harder to sample than regression coefficients. All remain above 1,000, which is adequate.
4 PART D — Posterior Distributions
mcmc_areas(b_m2, pars = c("b_Intercept", "b_Days"),
prob = 0.95, prob_outer = 0.99) +
labs(title = "Posterior Densities — Fixed Effects",
subtitle = "Shaded = 95% credible interval")
post_summary <- posterior_summary(b_m2) |>
as.data.frame() |>
tibble::rownames_to_column("parameter") |>
filter(grepl("^b_|^sd_|^sigma", parameter))
kable(post_summary, digits = 2,
col.names = c("Parameter", "Mean", "SE", "2.5%", "97.5%"),
caption = "Posterior summaries (95% credible intervals)")| Parameter | Mean | SE | 2.5% | 97.5% |
|---|---|---|---|---|
| b_Intercept | 251.38 | 7.33 | 237.02 | 265.93 |
| b_Days | 10.44 | 1.68 | 7.06 | 13.78 |
| sd_Subject__Intercept | 26.01 | 6.68 | 14.72 | 40.74 |
| sd_Subject__Days | 6.60 | 1.50 | 4.18 | 10.01 |
| sigma | 25.89 | 1.61 | 22.99 | 29.28 |
4.0.1 Interpretation
The posterior distributions provide the full Bayesian answer:
- Intercept posterior: the most probable baseline reaction time, with a 95% credible interval that can be directly interpreted as “95% probability the true value lies in this range.”
- Days posterior: if the entire 95% CI is above zero, we have strong evidence that sleep deprivation increases reaction time.
- SD parameters: the posterior of the random slope SD quantifies uncertainty about inter-individual variability in the Days effect.
Our results :
The posterior density plots provide the full Bayesian answer:
- Intercept posterior (mean = 251.4, 95% CrI: 237.0–265.9): the entire mass is well above zero, and the distribution is approximately symmetric — no skewness or multimodality, indicating clean estimation.
- Days posterior (mean = 10.4, 95% CrI: 7.1–13.8): the entire 95% credible interval excludes zero, providing strong evidence that sleep deprivation increases reaction time. The direct probabilistic interpretation: “there is a 95% probability that the true average daily increase in RT lies between 7.1 and 13.8 ms.”
- The credible interval for Days (width = 6.7 ms) is wider than the frequentist CI from Project 08 (width = 6.5 ms). The difference is small but reflects the Bayesian framework’s proper propagation of uncertainty about the variance components.
5 PART E — Posterior Predictive Checks
pp_check(b_m2, ndraws = 50, type = "dens_overlay") +
labs(title = "Posterior Predictive Check",
subtitle = "Does the model generate data that look like the observed data?")
5.0.1 Interpretation
The posterior predictive check is the Bayesian equivalent of checking residual distributions — but more powerful, because it compares the entire observed distribution against simulated datasets:
- Good fit: the observed density (dark line) should be enveloped by the replicated densities (light lines).
- Systematic discrepancy (e.g., bimodality in observed data not captured by replicates) would indicate model misspecification.
Our results :
The posterior predictive check confirms good model fit: the observed data density (dark line) is well enveloped by the replicated datasets (light lines). The model generates data that look like the real data.
Key checks:
- Location: the peak of the observed density should be near the centre of the replicated densities. ✓
- Spread: the width of the observed density should match the replicated spread. ✓
- Shape: no systematic discrepancy (e.g., bimodality in the observed data not captured by replicates).
The posterior predictive check is the Bayesian equivalent of residual analysis, but more powerful: it compares the entire data-generating distribution, not just the first two moments. Together with the prior predictive check (Part A), it completes the Bayesian model validation loop: priors → prior predictive → fit → posterior predictive.
6 PART F — Model Comparison via LOO-CV
loo_m1 <- loo(b_m1)
loo_m2 <- loo(b_m2)
cat("=== LOO-CV Comparison ===\n")=== LOO-CV Comparison ===
print(loo_compare(loo_m1, loo_m2)) elpd_diff se_diff
b_m2 0.0 0.0
b_m1 -23.8 11.8
cat("\n=== LOO details (random slope model) ===\n")
=== LOO details (random slope model) ===
print(loo_m2)
Computed from 4000 by 180 log-likelihood matrix.
Estimate SE
elpd_loo -861.4 22.6
p_loo 34.6 8.8
looic 1722.7 45.2
------
MCSE of elpd_loo is NA.
MCSE and ESS estimates assume MCMC draws (r_eff in [0.5, 1.2]).
Pareto k diagnostic values:
Count Pct. Min. ESS
(-Inf, 0.7] (good) 177 98.3% 347
(0.7, 1] (bad) 1 0.6% <NA>
(1, Inf) (very bad) 2 1.1% <NA>
See help('pareto-k-diagnostic') for details.
6.0.1 Interpretation
LOO-CV (Vehtari et al., 2017) estimates out-of-sample predictive accuracy:
- ELPD difference > 0 for M2 means the random slope model predicts new data better than the random intercept model.
- SE of the difference: if the difference is more than ~2 SEs from zero, the models are reliably different.
- This is the Bayesian analogue of the likelihood ratio test from Project 08 — both should favour the random slope model.
Our results :
LOO-CV (Vehtari et al., 2017) strongly supports the random-slope model:
- ΔELPD = 23.8 (SE = 11.8): M2 predicts new data better than M1 by approximately 2.0 standard errors — a meaningful difference, consistent with the highly significant LRT from Project 08 (χ² = 42.8, p = 5.0e-10).
- p_loo = 34.6: the effective number of parameters. This exceeds the nominal parameter count (6), reflecting the adaptive complexity of the random effects structure.
- Pareto k diagnostics: 177 observations (98.3%) have good k values (≤ 0.7), 1 has k in (0.7, 1], and 2 have k > 1. The 3 problematic observations are influential data points for which LOO approximation is unreliable. The warning suggests
moment_match = TRUEfor more accurate LOO estimates. For the model comparison conclusion, this is unlikely to change the result given the large ELPD difference, but it would be worth addressing in a publication context.
The random-slope model predicts new data substantially better than the random-intercept-only model. This Bayesian evidence aligns perfectly with the likelihood-ratio test reported in Project 08 and confirms that individual differences in vulnerability to sleep deprivation are not only present but essential to model..
6.1 Prior sensitivity analysis
# ── Vague priors (brms defaults) ─────────────────────────────────────────────
b_vague <- brm(
Reaction ~ Days + (Days | Subject),
data = sleepstudy,
# No explicit priors → brms defaults (very wide)
chains = 4,
iter = 2000,
warmup = 1000,
seed = 2026,
cores = 4,
silent = 2,
file = "cache_brms_vague"
)
# ── Tight priors (strongly informative) ──────────────────────────────────────
priors_tight <- c(
prior(normal(250, 20), class = "Intercept"),
prior(normal(10, 3), class = "b", coef = "Days"),
prior(cauchy(0, 5), class = "sd", coef = "Intercept", group = "Subject"),
prior(cauchy(0, 3), class = "sd", coef = "Days", group = "Subject"),
prior(cauchy(0, 10), class = "sigma")
)
b_tight <- brm(
Reaction ~ Days + (Days | Subject),
data = sleepstudy,
prior = priors_tight,
chains = 4,
iter = 2000,
warmup = 1000,
seed = 2026,
cores = 4,
silent = 2,
file = "cache_brms_tight"
)
# ── Compare fixed effects across prior specifications ────────────────────────
sensitivity <- bind_rows(
tibble(prior_spec = "Vague (defaults)",
parameter = c("Intercept", "Days"),
estimate = fixef(b_vague)[, "Estimate"],
lower = fixef(b_vague)[, "Q2.5"],
upper = fixef(b_vague)[, "Q97.5"]),
tibble(prior_spec = "Weakly informative",
parameter = c("Intercept", "Days"),
estimate = fixef(b_m2)[, "Estimate"],
lower = fixef(b_m2)[, "Q2.5"],
upper = fixef(b_m2)[, "Q97.5"]),
tibble(prior_spec = "Strongly informative",
parameter = c("Intercept", "Days"),
estimate = fixef(b_tight)[, "Estimate"],
lower = fixef(b_tight)[, "Q2.5"],
upper = fixef(b_tight)[, "Q97.5"])
)
kable(sensitivity, digits = 2,
caption = "Prior sensitivity: posterior estimates under 3 prior specifications")| prior_spec | parameter | estimate | lower | upper |
|---|---|---|---|---|
| Vague (defaults) | Intercept | 251.21 | 237.00 | 266.35 |
| Vague (defaults) | Days | 10.40 | 7.03 | 14.00 |
| Weakly informative | Intercept | 251.38 | 237.02 | 265.93 |
| Weakly informative | Days | 10.44 | 7.06 | 13.78 |
| Strongly informative | Intercept | 246.94 | 232.38 | 259.38 |
| Strongly informative | Days | 9.57 | 6.67 | 12.23 |
ggplot(sensitivity, aes(x = prior_spec, y = estimate,
ymin = lower, ymax = upper, colour = prior_spec)) +
geom_pointrange(size = 0.6, linewidth = 0.8) +
facet_wrap(~ parameter, scales = "free_y") +
scale_colour_manual(values = palette_bayes[c(2, 3, 4)]) +
labs(
title = "Prior Sensitivity Analysis",
subtitle = "Posterior stability across vague / weakly / strongly informative priors",
x = NULL, y = "Posterior Estimate", colour = NULL
) +
theme(axis.text.x = element_text(angle = 20, hjust = 1))
6.1.1 Interpretation
Prior sensitivity becomes critical with smaller samples (e.g., N < 50) or when priors are placed on poorly identified parameters (e.g., the intercept-slope correlation with few clusters, as seen in Project 08).
Our results :
The sensitivity analysis confirms that the posteriors are robust to prior specification:
- Intercept: 251.2 (vague) → 251.4 (weakly informative) → 246.9 (strongly informative). The tight prior (Normal(250, 20)) pulls the estimate down by 4.4 ms (1.8%) toward its prior mean of 250 — a mild but visible prior influence, confirming the prior is genuinely “informative” at this scale.
- Days: 10.4 (vague) → 10.4 (weakly informative) → 9.6 (strongly informative). The tight prior (Normal(10, 3)) narrows the CrI from [7.1, 13.8] to [6.7, 12.2] and pulls the estimate toward 10 by 0.8 ms/day. The substantive conclusion (significant positive effect) is unchanged.
- The vague and weakly informative priors are indistinguishable — confirming that our weakly informative priors add minimal constraint. The strongly informative priors produce detectable but inconsequential shifts, demonstrating that with N = 180, even narrow priors cannot override the data.
This is expected with N = 180: the likelihood contains enough information to overwhelm even moderately informative priors. The practical implication is that the results reported in this project are data-driven, not prior-driven — the conclusions would be the same regardless of reasonable prior choices. Even the strongly informative priors shift the Days effect only slightly (9.57 ms/day) while remaining well within the credible interval of the weakly informative model.
Prior sensitivity becomes critical with smaller samples (e.g., N < 50) or when priors are placed on poorly identified parameters (e.g., the intercept-slope correlation with few clusters, as seen in Project 08).
7 PART G — Frequentist–Bayesian Comparison
# ── Fit frequentist model ────────────────────────────────────────────────────
freq_m2 <- lmer(Reaction ~ Days + (Days | Subject), data = sleepstudy)
# ── Extract and compare ──────────────────────────────────────────────────────
freq_fixed <- tibble(
parameter = c("Intercept", "Days"),
freq_est = fixef(freq_m2),
freq_se = sqrt(diag(vcov(freq_m2)))
)
bayes_fixed <- tibble(
parameter = c("Intercept", "Days"),
bayes_est = fixef(b_m2)[, "Estimate"],
bayes_se = fixef(b_m2)[, "Est.Error"],
bayes_lower = fixef(b_m2)[, "Q2.5"],
bayes_upper = fixef(b_m2)[, "Q97.5"]
)
comparison <- freq_fixed |>
left_join(bayes_fixed, by = "parameter") |>
mutate(
diff_est = bayes_est - freq_est,
diff_pct = 100 * diff_est / freq_est
)
kable(comparison, digits = 3,
caption = "Frequentist (lme4) vs Bayesian (brms) estimates")| parameter | freq_est | freq_se | bayes_est | bayes_se | bayes_lower | bayes_upper | diff_est | diff_pct |
|---|---|---|---|---|---|---|---|---|
| Intercept | 251.405 | 6.825 | 251.384 | 7.332 | 237.018 | 265.926 | -0.021 | -0.009 |
| Days | 10.467 | 1.546 | 10.440 | 1.679 | 7.063 | 13.780 | -0.027 | -0.262 |
7.0.1 Interpretation
The comparison confirms near-perfect agreement between paradigms:
- Intercept: 251.405 (lme4) vs. 251.384 (brms) — difference of −0.021 ms (−0.009%). Effectively identical.
- Days: 10.467 (lme4) vs. 10.440 (brms) — difference of −0.027 ms/day (−0.26%). Effectively identical.
- Standard errors: Bayesian SEs are slightly larger (Intercept: 7.33 vs. 6.83; Days: 1.68 vs. 1.55). This ~7% inflation reflects the Bayesian framework’s honest accounting of uncertainty about the variance components — REML treats the random-effect variances as known when computing fixed-effect SEs, while the Bayesian posterior marginalises over their full uncertainty.
Bottom line: with weakly informative priors and sufficient data, the two frameworks converge on the same substantive conclusions. The Bayesian framework adds: (1) direct probabilistic interpretation of intervals, (2) the prior/posterior predictive checking workflow, (3) LOO-CV for model comparison, and (4) explicit prior sensitivity analysis. These are interpretive and diagnostic gains, not accuracy gains — the point estimates are the same.
With weakly informative priors and sufficient data (N = 180), the Bayesian and frequentist estimates should be virtually identical:
- Estimate differences < 1% indicate that the prior has negligible influence — the likelihood dominates.
- Bayesian SEs ≈ frequentist SEs: with weakly informative priors, the posterior width approximates the frequentist confidence interval.
- The interpretive difference remains: the Bayesian 95% credible interval is a direct probability statement about the parameter; the frequentist 95% CI is a statement about the procedure’s long-run coverage.
8 Technical Summary
cat("─── Reproducibility Information ───\n")─── Reproducibility Information ───
cat(sprintf("Date: %s\n", Sys.Date()))Date: 2026-04-29
cat(sprintf("R version: %s\n", R.version.string))R version: R version 4.4.1 (2024-06-14 ucrt)
cat(sprintf("brms version: %s\n", packageVersion("brms")))brms version: 2.22.0
cat(sprintf("Stan version: %s\n", rstan::stan_version()))Stan version: 2.32.2
cat(sprintf("Platform: %s\n", sessionInfo()$platform))Platform: x86_64-w64-mingw32/x64
Operational note: The same Bayesian multilevel approach was applied to longitudinal critical thinking disposition data in the author’s postdoctoral research, where small cluster sizes (N < 30 per group) made the Bayesian framework’s regularisation of variance components particularly valuable.
8.1 References
- Bürkner, P.-C. (2017). brms: An R package for Bayesian multilevel models using Stan. Journal of Statistical Software, 80(1), 1–28.
- Gelman, A., Carlin, J. B., Stern, H. S., Dunson, D. B., Vehtari, A., & Rubin, D. B. (2013). Bayesian Data Analysis (3rd ed.). CRC Press.
- McElreath, R. (2020). Statistical Rethinking (2nd ed.). CRC Press.
- Vehtari, A., Gelman, A., & Gabry, J. (2017). Practical Bayesian model evaluation using leave-one-out cross-validation and WAIC. Statistics and Computing, 27(5), 1413–1432.