Fits a Bayesian random-effects meta-analysis in which the true study effects
arise from a mixture of M normal components, each with its own mean and
between-study heterogeneity (Maier, 2026). With M = 1 the model is the
ordinary random-effects meta-analysis. No publication bias adjustment is
applied; see sel_mix() and sel_flexpb() for the corresponding selection
models.
Arguments
- y
numeric vector of observed effect sizes.
- sd
numeric vector of the standard errors of
y(same length asy).- M
number of mixture components (a positive integer).
- mu_sd
standard deviation of the normal prior on the component means (used by both mean-prior settings: as the prior on every ordered mean when
mean_prior = "normal", and as the prior on the first mean whenmean_prior = "gap"). Defaults to1.- tau_sd
standard deviation of the half-normal prior on the heterogeneity SD
tau(used whentau_prior = "half_normal"). Defaults to0.2.- mean_prior
prior family for the component means.
"normal"(default) places an independent normal(0,mu_sd) prior on each ordered mean."gap"uses a lognormal prior on the positive gaps between adjacent means (controlled bygap_meanlog,gap_sdlog,gap_min), which discourages two components collapsing onto the same location.- tau_prior
prior family for the heterogeneity SD
tau."half_normal"(default) uses a half-normal(0,tau_sd) prior."inv_gamma"places an inverse-gamma(tau_alpha,tau_beta) prior directly ontau.- gap_meanlog
log-scale location of the repulsive lognormal gap prior (only used when
mean_prior = "gap"). Defaults tolog(0.2).- gap_sdlog
log-scale standard deviation of the repulsive lognormal gap prior (only used when
mean_prior = "gap"). Defaults to0.3.- gap_min
hard lower floor on the gaps between adjacent means: each gap is constrained to be at least
gap_minand the lognormal gap prior is truncated to[gap_min, Inf)(only used whenmean_prior = "gap"). Defaults to0.- tau_alpha
shape of the inverse-gamma prior on
tau(only used whentau_prior = "inv_gamma"). Defaults to2.5.- tau_beta
scale of the inverse-gamma prior on
tau(only used whentau_prior = "inv_gamma"). Defaults to0.15.- prior_only
if
TRUE, the data are ignored and the prior distribution is sampled instead (useful for prior predictive checks).- ...
further arguments passed to
rstan::sampling(), e.g.chains,iter,cores,seed,refreshorcontrol.
Value
An object of class stanfit as returned by
rstan::sampling(). It contains posterior draws of
muvector of length
M: the ordered component means.tauvector of length
M: the between-study standard deviation of each component.thetavector of length
M: the mixing weights.mu1,mu_gapthe underlying parameterisation of the means (the first mean and the
M - 1gaps between adjacent means).log_taulog(tau), convenient for funnel diagnostics.posterior_probsKbyMmatrix: the posterior probability that studyibelongs to componentm.y_rep,sd_repposterior predictive draws of
Keffect sizes and the standard errors they were generated with. For each draw a component is sampled fromtheta, a study is sampled with probability proportional to its responsibility for that component, and a new effect size is generated using that study's standard error.
Use e.g. print(fit, pars = c("mu", "tau", "theta")), summary(fit) or
rstan::extract() to access them.
References
Maier, M. (2026). Addressing heterogeneity with Bayesian meta-analytic mixture modelling. PsyArXiv. doi:10.31234/osf.io/nkyqm_v2
Maier, M., Bartoš, F., & Wagenmakers, E.-J. (2023). Robust Bayesian meta-analysis: Addressing publication bias with model-averaging. Psychological Methods, 28(1), 107–122. doi:10.1037/met0000405
Stan Development Team (2024). RStan: the R interface to Stan. https://mc-stan.org/
See also
sel_mix() and sel_flexpb() for the same mixture model combined
with step-function selection models; sim_mix() to simulate data from
the model.
Other model fitting functions:
sel_flexpb(),
sel_mix()
Examples
# \donttest{
# Simulate 40 studies from a two-component mixture without publication bias
set.seed(1)
dat <- sim_mix(K = 40, M = 2, mu = c(0, 0.6), tau = c(0.1, 0.1),
steps = c(0.95, 0.975), weights = c(1, 1, 1), one_sided = TRUE)
# A single short chain keeps the example fast; use e.g. chains = 4 and
# iter = 2000 (the rstan defaults) for a real analysis.
fit <- re_mix(dat$y, dat$sds, M = 2, chains = 1, iter = 500, refresh = 0)
#> Warning: The largest R-hat is 1.08, indicating chains have not mixed.
#> Running the chains for more iterations may help. See
#> https://mc-stan.org/misc/warnings.html#r-hat
#> Warning: Bulk Effective Samples Size (ESS) is too low, indicating posterior means and medians may be unreliable.
#> Running the chains for more iterations may help. See
#> https://mc-stan.org/misc/warnings.html#bulk-ess
#> Warning: Tail Effective Samples Size (ESS) is too low, indicating posterior variances and tail quantiles may be unreliable.
#> Running the chains for more iterations may help. See
#> https://mc-stan.org/misc/warnings.html#tail-ess
print(fit, pars = c("mu", "tau", "theta"))
#> Inference for Stan model: random_effects_mix.
#> 1 chains, each with iter=500; warmup=250; thin=1;
#> post-warmup draws per chain=250, total post-warmup draws=250.
#>
#> mean se_mean sd 2.5% 25% 50% 75% 97.5% n_eff Rhat
#> mu[1] 0.06 0.03 0.15 -0.45 0.01 0.06 0.16 0.33 22 1.08
#> mu[2] 0.52 0.02 0.15 0.27 0.43 0.53 0.59 0.84 63 1.00
#> tau[1] 0.16 0.01 0.10 0.01 0.08 0.14 0.22 0.35 78 1.00
#> tau[2] 0.15 0.02 0.10 0.02 0.07 0.12 0.23 0.36 29 1.02
#> theta[1] 0.49 0.03 0.21 0.02 0.38 0.50 0.60 0.93 43 1.00
#> theta[2] 0.51 0.03 0.21 0.07 0.40 0.50 0.62 0.98 43 1.00
#>
#> Samples were drawn using NUTS(diag_e) at Fri Oct 9 17:03:49 2026.
#> For each parameter, n_eff is a crude measure of effective sample size,
#> and Rhat is the potential scale reduction factor on split chains (at
#> convergence, Rhat=1).
# Posterior probability that each study belongs to the second component
resp <- rstan::extract(fit, pars = "posterior_probs")$posterior_probs
head(colMeans(resp)[, 2])
#> [1] 0.8760180 0.2089365 0.4848856 0.1406083 0.8657916 0.1791249
# Sample from the prior only (no data enter the model)
prior <- re_mix(dat$y, dat$sds, M = 2, prior_only = TRUE,
chains = 1, iter = 500, refresh = 0)
#> Warning: Bulk Effective Samples Size (ESS) is too low, indicating posterior means and medians may be unreliable.
#> Running the chains for more iterations may help. See
#> https://mc-stan.org/misc/warnings.html#bulk-ess
#> Warning: Tail Effective Samples Size (ESS) is too low, indicating posterior variances and tail quantiles may be unreliable.
#> Running the chains for more iterations may help. See
#> https://mc-stan.org/misc/warnings.html#tail-ess
print(prior, pars = c("mu", "tau"))
#> Inference for Stan model: random_effects_mix.
#> 1 chains, each with iter=500; warmup=250; thin=1;
#> post-warmup draws per chain=250, total post-warmup draws=250.
#>
#> mean se_mean sd 2.5% 25% 50% 75% 97.5% n_eff Rhat
#> mu[1] -0.48 0.06 0.77 -1.94 -1.02 -0.44 0.10 1.03 143 1
#> mu[2] 0.60 0.06 0.88 -1.06 0.07 0.58 1.19 2.27 204 1
#> tau[1] 0.16 0.01 0.11 0.00 0.08 0.14 0.23 0.41 289 1
#> tau[2] 0.15 0.01 0.12 0.00 0.06 0.13 0.23 0.39 92 1
#>
#> Samples were drawn using NUTS(diag_e) at Fri Oct 9 17:03:49 2026.
#> For each parameter, n_eff is a crude measure of effective sample size,
#> and Rhat is the potential scale reduction factor on split chains (at
#> convergence, Rhat=1).
# }