In screening programs, individuals are periodically tested for a disease, such as cancer, before it becomes symptomatic. Two questions are usually of interest. First, what proportion of individuals already has the (pre-)disease at the start of follow-up, before any incident disease can develop? These cases are called prevalent. Second, at what rate do initially disease-free individuals develop the disease over time? This is the incidence. Because screening tests are imperfect, prevalent and incident cases are not directly observed: a truly positive individual may be missed at a screening round (imperfect sensitivity), and a positive baseline test cannot by itself distinguish a long-standing prevalent case from a very early incident one.
Prevalence-incidence models are often used to disentangle
prevalence from incidence, that is, to estimate an incidence (cumulative
distribution) function as if there were no prevalence at baseline. This
describes disease progression in the initially healthy sub-population.
In many applications, however, a joint representation is also
useful, in which prevalence enters the cumulative incidence function as
a point mass at time zero and incident events accumulate thereafter.
BayesPIM estimates both representations from the same
fitted model; they are compared in the posterior-estimation Section
@ref(sec-cifs).
BayesPIM implements the Bayesian prevalence-incidence
mixture (PIM) model of Klausch et al. (2026). Time to incidence is
modeled by an accelerated failure time (AFT) specification. With linear
predictor \(\eta_i =
\mathbf{x}_{ti}'\boldsymbol{\beta}_t\), covariates multiply
event times by \(\exp(\eta_i)\)
relative to a baseline distribution. For the Weibull, log-normal, and
log-logistic families this is the familiar log-location-scale form
\[\begin{equation} \log t_i = \eta_i + \sigma_t \epsilon_i, (\#eq:aft) \end{equation}\]
and gamma and Prentice generalized-gamma families are also available (Section @ref(sec-information-criteria)). Baseline prevalence is modeled with a probit link. Writing \(g_i = 1\) for a prevalent individual and \(g_i = 0\) otherwise, and using a latent continuous variable \(w_i\),
\[\begin{equation} \Pr(g_i = 1 \mid \mathbf{x}_{gi}) = \Pr(w_i > 0 \mid \mathbf{x}_{gi}), \qquad w_i = \mathbf{x}_{gi}'\boldsymbol{\beta}_g + \psi_i, (\#eq:probit) \end{equation}\]
where \(\psi_i\) is a standard normal error. Finally, imperfect test sensitivity is captured by a parameter \(\kappa\), the probability that a truly positive individual is detected at a screening round. \(\kappa\) can be fixed at a known value or estimated (Section @ref(sec-kappa)). The model is estimated with a Bayesian Gibbs sampler, using weakly informative default priors that regularize the likelihood when advanced-state events are infrequent, which is common in screening data.
An analysis with BayesPIM consists of the following
steps, using dedicated functions:
bayespim)get_ic)summary.bayespim,
plot.bayespim, ppCIF, and
plot.ppCIF)These are discussed in Section @ref(sec-model-estimation), Section @ref(sec-information-criteria), and Section @ref(sec-posterior-summaries). Additional options are discussed in Section @ref(sec-further-topics).
bayespimThe function bayespim estimates the parameters of the
model in equations @ref(eq:aft) and @ref(eq:probit) with a Gibbs
sampler. The observed data for a non-prevalent individual is a series of
negative screening tests followed either by a positive test or by right
censoring. The exact event time is therefore never observed; it is only
known to lie in one of the screening intervals. Moreover, because a test
can miss a truly positive individual with probability \(1-\kappa\), even the interval containing
the event is uncertain. Bayesian data augmentation handles this missing
information by imputing plausible latent quantities and then updating
the model parameters given these quantities.
Klausch et al. (2026) originally augmented the exact latent
event time \(t_i\) for each individual
and updated the incidence parameters with a Metropolis-Hastings step on
the resulting complete-data likelihood. The current version of
BayesPIM instead uses a collapsed slice sampler,
which is the default (sampler = "slice_collapsed"). Rather
than imputing an exact time, the collapsed sampler augments only the
latent screening interval \((v_{i,k-1}, v_{ik}]\) in which the
(possibly missed) event occurred, and updates the incidence parameters
from the interval-censored likelihood, marginalizing over the exact
position of \(t_i\) within the
interval. Marginalizing over \(t_i\) in
this way is known as collapsing (Liu, 1994). A collapsed Gibbs
sampler preserves the target posterior but reduces the autocorrelation
that exact-time augmentation induces, which yields faster convergence.
This is why the collapsed sampler replaced the original
Metropolis-Hastings sampler as the default; the earlier samplers remain
available (Section @ref(sec-samplers)).
The main steps of the Gibbs sampler are, after initialization:
These steps are run repeatedly. After a warm-up period, the sampler yields auto-correlated draws from the target posterior distribution of the parameters. Conditional on the augmented intervals and prevalence indicators, the incidence parameters are drawn from the complete-data posterior
\[\begin{align} q(\boldsymbol{\beta}_t, \sigma_t \mid \cdot) \propto L(\boldsymbol{\beta}_t, \sigma_t \mid \cdot)\,\pi(\boldsymbol{\beta}_t, \sigma_t), \end{align}\]
where \(L\) is the interval-censored complete-data likelihood and \(\pi\) the prior density (Section @ref(sec-prior-assumptions)). For an individual whose event has been augmented into interval \((v_{k-1}, v_k]\), the likelihood contribution is the probability that the event falls in that interval, \(F_t(v_k) - F_t(v_{k-1})\), where \(F_t\) is the cumulative distribution function of the incidence distribution. The full derivation is given in Klausch et al. (2026).
bayespim input data structurebayespim accepts four data inputs: v_obs,
x_t, x_g, and r. These have to be
available for all individuals in the data; missing information has to be
handled outside of BayesPIM.
Argument v_obs provides the observed screening times as
a list of length \(n\),
one numeric vector per individual. Every vector starts with
0, the baseline time. The remaining entries encode the
screening series and its outcome:
c(0).c(0, Inf).Inf for right censoring
(e.g. c(0, 22, 48, 77, Inf)) or ends at the time of the
positive test if an event is detected
(e.g. c(0, 23, 52, 78, 105, 132)).Because a longer screening series carries no extra information under
the model’s assumptions, only the times up to the event or to the last
follow-up need to be supplied. Argument r is a binary
vector of length \(n\) indicating
whether the baseline test was actually carried out
(r[i] = 1) or is missing (r[i] = 0). Together,
v_obs and r encode which cases are known
prevalent (i.e. positive test at baseline), incident, or right censored,
and whether their baseline status is known.
Arguments x_t and x_g take the covariates
for the incidence and prevalence models, respectively, each an \(n \times p\) matrix. The two
sets of covariates may differ, although in practice the same covariates
are often used for both. If no covariates are supplied,
x_t = NULL and/or x_g = NULL give
intercept-only models. Categorical variables have to be dummy-coded by
the user; stats::model.matrix is useful for this.
By default, standardize_covariates = TRUE centers each
non-binary covariate at its sample mean and divides it by its sample
standard deviation before fitting. A column with exactly two observed
values is detected as binary/dummy and is left unchanged. This
standardization generally improves sampling when continuous covariates
have very different scales. The returned chains and summaries are
transformed back to the original covariate scale, and the original
matrices remain in $x_t and $x_g. Set
standardize_covariates = FALSE to fit directly on the
supplied scale. Numerically coded categorical variables with more than
two levels must still be dummy-coded; otherwise they are treated as
continuous.
BayesPIM has a built-in data-generating function
gen_data that simulates data under the model of Klausch et
al. (2026); see ?gen_data for all options. We use it to
illustrate the input structure and model estimation.
library(BayesPIM)
# Generate data under the PIM of Klausch et al. (2026)
set.seed(2025)
dat <- gen_data(
kappa = 0.7, # Test sensitivity
n = 1e3, # Sample size
theta = 0.2, # Baseline prevalence when all covariates are zero
p = 1, # Number of continuous covariates
p_discrete = 1, # Add one Bernoulli(0.5) covariate
beta_t = c(0.2, 0.2), # True incidence slopes
beta_g = c(0.2, 0.2), # True prevalence slopes
mu_t = 5, # True incidence intercept
sigma_t = 0.2, # True incidence AFT scale
dist = "weibull", # Incidence distribution
v_min = 20, # Minimum time between screening moments
v_max = 30, # Maximum time between screening moments
mean_rc = 80, # Mean time to right censoring (exponential)
prob_r = 1 # Probability that a baseline test is done
)gen_data simulates the incidence times from the AFT
model in equation @ref(eq:aft), with intercept mu_t and
slopes beta_t, and generates prevalence from the probit
model in equation @ref(eq:probit), with intercept
qnorm(theta) (so theta is the prevalence
probability when all covariates are zero) and slopes
beta_g. A screening series is then superimposed: successive
screening times are drawn uniformly between v_min and
v_max apart, and the right-censoring time is exponential
with mean mean_rc. At each screening time after the true
event, a test detects the event with probability kappa.
Besides weibull, the distributions lognormal,
loglog (log-logistic), gamma, and
gengamma (Prentice generalized gamma) are available.
The screening times are returned in dat$v_obs, in
exactly the format bayespim expects. Looking at the first
individuals illustrates the coding.
[[1]]
[1] 0
[[2]]
[1] 0.00000 22.53705 51.89452 78.35944 104.52213 131.74624
[[3]]
[1] 0.00000 25.37066
[[4]]
[1] 0
[[5]]
[1] 0.00000 27.37410 53.60168 80.54386 108.43628 Inf
[[6]]
[1] 0.00000 22.31108 48.06398 77.10746 Inf
Individuals 1 and 4 have c(0) and are known prevalent
(due to a positive baseline test). Individual 2 has an event detected at
time 131.7, and individual 3 at time 25.4.
Individuals 5 and 6 end in Inf and are right-censored
without a detected event. In addition, gen_data returns the
covariate matrix dat$x, the baseline-test indicator
dat$r, and the latent quantities
dat$times_true (true event times), dat$g (true
prevalence status), and dat$prob_g (true prevalence
probabilities), which are unobserved in practice and are not passed to
bayespim.
Before running the sampler, it is useful to inspect the case mix.
v <- dat$v_obs
prevalent <- vapply(v, function(x) length(x) == 1L, logical(1))
censored <- vapply(v, function(x) length(x) > 1L && is.infinite(x[length(x)]), logical(1))
c(prevalent = sum(prevalent),
incident = sum(!prevalent & !censored),
censored = sum(censored))prevalent incident censored
181 209 610
About 18% of individuals are prevalent and 21% have a detected
incident event, which is ample for bayespim to run
successfully. Care in model specification (number of covariates and
choice of distribution) should be taken when advanced-state events are
infrequent. BayesPIM is designed to cope with sparse events
and usually converges if run long enough, but the precision of
estimation may then be low, especially with many covariates.
Klausch et al. (2026) showed that weakly informative priors help to
obtain stable estimates even when events are infrequent and the
likelihood is nearly flat. bayespim uses such priors by
default. For the incidence regression coefficients \(\boldsymbol{\beta}_t\), a normal prior
(beta_prior = 'norm') is used by default, with standard
deviation tau_t (default 1); a Student-\(t\) prior is available with
beta_prior = 't', in which case tau_t sets the
degrees of freedom. A half-normal prior with standard deviation
sig_prior (default 1) is placed on the
positive scale/dispersion parameter \(\sigma_t\). For the prevalence coefficients
\(\boldsymbol{\beta}_g\), a
zero-centered normal prior with standard deviation tau_g
(default 1) is used. In the generalized-gamma model, a
zero-centered normal prior with standard deviation
q_prior_sd is placed on the signed shape parameter \(Q\). With the default covariate
standardization, tau_t and tau_g apply to
effects per one-standard-deviation change in a continuous covariate,
while binary effects remain contrasts between their two supplied values.
The internal intercept concerns an individual at the mean
continuous-covariate values; returned intercepts are transformed back to
covariate value zero.
The posterior can be sensitive to these choices when data are sparse,
so a prior sensitivity analysis can be useful; see Klausch et al. (2026)
for an example. Users can also supply a custom log-prior through
log_prior_fun; see Section @ref(sec-user-defined-prior) and
?log_aft_prior.
bayespim runWe now fit a Weibull model to the simulated data. The incidence
covariates are passed to x_t, the prevalence covariates to
x_g, and the baseline-test indicator to r.
Here the test sensitivity is known, so we set kappa = 0.7
and update_kappa = FALSE; estimating \(\kappa\) is discussed in Section
@ref(sec-kappa). The sampler runs chains = 4 MCMC chains in
parallel, which is needed for reliable convergence diagnostics and to
generate draws efficiently. The user’s machine should have at least
chains free CPUs available.
mod_slice <- bayespim(
v_obs = dat$v_obs,
x_t = dat$x,
x_g = dat$x,
r = dat$r,
kappa = 0.7,
update_kappa = FALSE,
ndraws = 1e3,
warmup = 5e2,
save_every = 1,
standardize_covariates = TRUE,
chains = 4,
seed_chains = 1:4,
min_effss = 800,
update_till_converge = FALSE,
sampler = "slice_collapsed",
dist = "weibull"
)The desired incidence distribution is passed via dist.
The basic behavior of the Gibbs sampler is controlled by
ndraws, warmup, chains,
save_every, and seed_chains. The call above
runs ndraws = 1000 iterations per chain and then stops
(update_till_converge = FALSE). Each chain is randomly
initialized, but the initialization and sampling are reproducible
through seed_chains: one integer seed per chain, here
seed_chains = 1:4 for chains = 4. Repeating
the run with the same seeds reproduces the posterior chains exactly;
seed_chains = NULL initializes the chains randomly.
A good warmup value is not known a priori, so it is
advisable to run the sampler for a moderate number of iterations first
and inspect the trace plots (Section @ref(sec-parameter-summaries)) to
choose it.
By default, save_every = 1 stores every generated
parameter draw in the returned $par chains. Convergence
diagnostics and summary discard the first
warmup generated iterations and then use every stored
post-warm-up draw. warmup is always specified on the
generated-iteration scale, independently of save_every: for
example, warmup = 500 and save_every = 10 omit
the 50 stored draws at iterations 10 through 500 in each chain, and the
first retained draw is iteration 510. For values larger than one only
every save_every-th draw is retained in memory and returned
as $par; for example save_every = 10 saves
every 10th posterior draw in each chain in memory. Such ‘thinning’
reduces memory and chain auto-correlation but decreases precision of
estimation of posterior statistics and convergence diagnostics. If
storing all draws would exceed available memory, especially when
update_till_converge = TRUE may append several large
updates, set save_every to a value greater than one.
Storage selection does not alter the Markov-chain trajectory, but the
intervening parameter draws are permanently discarded and diagnostics
and summaries can then use only the stored draws. The setting is
inherited by manual and automatic updates. Because retaining all draws
generally gives the most informative diagnostics and posterior
estimates, values above one are recommended only when memory is a
practical constraint. Plotting has a separate thinning
argument that can reduce rendering cost without discarding draws from
the fitted object.
After the requested draws, bayespim evaluates and prints
convergence diagnostics: the rank-normalized split R-hat of Vehtari et
al. (2021) and the effective sample size (ESS), computed with the
posterior package (Bürkner et al., 2026). These are checked
against max_rhat (default 1.01) and
min_effss (default chains * 100). To make the
update workflow visible in this example, we use the stricter requirement
min_effss = 800 rather than the four-chain default of
400.
Convergence diagnostics after 1000 iterations per chain (500 stored post-warm-up draws used).
Convergence criteria: R-hat <= 1.010 and ESS >= 800.0
block parameter R_hat ESS
t beta_t_intercept 1.005 446.5
t beta_t_x_1 1.002 778.8
t beta_t_x_2 1.005 576.8
t sigma_t 1.010 554.4
g beta_g_intercept 1.004 549.7
g beta_g_x_1 1.006 678.0
g beta_g_x_2 1.005 631.1
The table reports diagnostics for the incidence (t) and
prevalence (g) parameter blocks. After 1000 draws all R-hat
values meet their threshold, but several effective sample sizes are
below the deliberately strict requirement of 800. We
therefore need more draws under this example’s convergence rule, which
can be added by updating the run (Section @ref(sec-updating-runs)) or by
letting bayespim update automatically (Section
@ref(sec-auto-convergence)). The runtime of the sampler is stored in the
fitted object.
Time difference of 4.020705 secs
bayespim runsIf an initial run has not converged, it can be continued by passing
the fitted object to the prev_run argument of a new
bayespim call. The chains resume from their last draws and
the new draws are merged with the previous run. The number of added
draws is controlled by ndraws_update (default: the original
ndraws).
Updating previous MCMC run.
Convergence diagnostics after 3000 iterations per chain (2500 stored post-warm-up draws used).
Convergence criteria: R-hat <= 1.010 and ESS >= 800.0
block parameter R_hat ESS
t beta_t_intercept 1.001 2186.3
t beta_t_x_1 1.002 4351.5
t beta_t_x_2 1.001 2685.5
t sigma_t 1.001 2672.2
g beta_g_intercept 1.001 2951.2
g beta_g_x_1 1.001 3355.5
g beta_g_x_2 1.001 3287.6
After a further 2000 iterations per chain (3000 in total), all R-hat
values are at most 1.002 and all effective sample sizes
exceed 2100, so the sampler has converged and we have ample
draws for posterior inference. Data, sampler, priors, covariate-scaling
constants, storage interval, and the end-of-chain random-number state
are inherited from prev_run; we only repeat the stricter
ESS criterion because convergence thresholds are call-specific. By
default the original warm-up is retained;
warmup_updated = TRUE instead increases the warm-up with
each update.
Instead of adding draws manually, bayespim can update
automatically until convergence with
update_till_converge = TRUE. If the initial
ndraws draws are not sufficient, another
ndraws_update draws are added and convergence is
re-evaluated, and so on, until the criteria (max_rhat,
min_effss) are met or the maximum number of draws
maxit (default Inf) is reached. This can be
requested directly, without a manual initial run.
mod_weibull <- bayespim(
v_obs = dat$v_obs,
x_t = dat$x,
x_g = dat$x,
r = dat$r,
kappa = 0.7,
update_kappa = FALSE,
ndraws = 1e3,
warmup = 5e2,
save_every = 1,
standardize_covariates = TRUE,
chains = 4,
seed_chains = 1:4,
min_effss = 800,
update_till_converge = TRUE,
ndraws_update = 1e3,
sampler = "slice_collapsed",
dist = "weibull"
)Not converged after 1000 iterations per chain; updating with 1000 iterations.
Convergence criteria: R-hat <= 1.010 and ESS >= 800.0
block parameter R_hat ESS
t beta_t_intercept 1.005 446.5
t beta_t_x_1 1.002 778.8
t beta_t_x_2 1.005 576.8
t sigma_t 1.010 554.4
g beta_g_intercept 1.004 549.7
g beta_g_x_1 1.006 678.0
g beta_g_x_2 1.005 631.1
Updating previous MCMC run.
Converged after 2000 iterations per chain (1500 stored post-warm-up draws used).
Convergence criteria: R-hat <= 1.010 and ESS >= 800.0
block parameter R_hat ESS
t beta_t_intercept 1.001 1275.2
t beta_t_x_1 1.002 2508.5
t beta_t_x_2 1.002 1651.9
t sigma_t 1.001 1660.3
g beta_g_intercept 1.002 1769.4
g beta_g_x_1 1.001 2044.3
g beta_g_x_2 1.002 1904.0
Here a single automatic update of 1000 iterations was enough to reach
convergence at 2000 iterations per chain. The whole fit, including the
automatic update, took about seven seconds. This example uses
save_every = 1, so all 2000 draws per chain are retained.
For a substantially longer automatic run where the growing
$par object might exceed available R memory, rerun the
model with, for example, save_every = 5; the fit will store
every fifth generated state, and convergence checks and summaries will
use all of those stored states without further thinning.
Time difference of 7.404977 secs
We use mod_weibull as the fitted Weibull model in the
remainder of this guide. If silent = TRUE is passed, the
progress output shown above is suppressed; the diagnostics are still
computed and stored in the convergence element of the
returned object.
bayespimIn practice, the correct incidence distribution is unknown, so
several models can be fitted and compared with information criteria. The
function get_ic computes the deviance information criterion
(DIC; Spiegelhalter et al., 2002) and two versions of the widely
applicable information criterion (WAIC-1 and WAIC-2; Watanabe, 2010), as
defined in Gelman et al. (2014). Lower values indicate better fit. The
DIC is most appropriate when the posterior is approximately normal; the
WAIC criteria are useful alternatives when some posteriors are skewed,
which can occur for BayesPIM.
Besides the two-parameter families (weibull,
loglog, lognormal, gamma), two
informative comparisons are the constrained exponential model
and the more flexible generalized-gamma model. The exponential
model is the special case of the Weibull with the AFT scale \(\sigma_t\) fixed at one, obtained with
dist = "weibull", fix_sigma = TRUE, and
sig_prior = 1. Fixing \(\sigma_t\) removes a parameter and
stabilizes estimation in sparse data, but assumes a constant hazard over
time (a Markov-type assumption). Conversely, the Prentice generalized
gamma (dist = "gengamma") has the Weibull, log-normal, and
gamma as special cases through its extra signed shape parameter \(Q\), and so is less constrained
than the Weibull. It is available only with the collapsed slice sampler,
for which convergence with the other samplers is typically slow.
We fit both alternatives to the same data, again updating till convergence.
# Exponential model (Weibull with sigma fixed at 1)
mod_exp <- bayespim(
v_obs = dat$v_obs, x_t = dat$x, x_g = dat$x, r = dat$r,
kappa = 0.7, update_kappa = FALSE,
ndraws = 1e3, warmup = 5e2, chains = 4, seed_chains = 5:8,
update_till_converge = TRUE, ndraws_update = 1e3,
sampler = "slice_collapsed", dist = "weibull",
fix_sigma = TRUE, sig_prior = 1
)Converged after 2000 iterations per chain (1500 stored post-warm-up draws used).
Convergence criteria: R-hat <= 1.010 and ESS >= 400.0 for sampled parameters; fixed parameters excluded: t:sigma_t
block parameter R_hat ESS
t beta_t_intercept 1.002 2023.4
t beta_t_x_1 1.001 3515.5
t beta_t_x_2 1.001 2171.6
g beta_g_intercept 1.003 1423.3
g beta_g_x_1 1.001 1533.1
g beta_g_x_2 1.002 1632.7
# Generalized-gamma model (Weibull is a special case)
mod_gg <- bayespim(
v_obs = dat$v_obs, x_t = dat$x, x_g = dat$x, r = dat$r,
kappa = 0.7, update_kappa = FALSE,
ndraws = 2e3, warmup = 1e3, chains = 4, seed_chains = 9:12,
update_till_converge = TRUE, ndraws_update = 2e3,
sampler = "slice_collapsed", dist = "gengamma"
)Converged after 4000 iterations per chain (3000 stored post-warm-up draws used).
Convergence criteria: R-hat <= 1.010 and ESS >= 400.0
block parameter R_hat ESS
t beta_t_intercept 1.002 865.4
t beta_t_x_1 1.001 4862.2
t beta_t_x_2 1.001 3053.9
t sigma_t 1.004 1331.2
t q_t 1.002 771.8
g beta_g_intercept 1.001 3702.2
g beta_g_x_1 1.001 4023.0
g beta_g_x_2 1.001 4139.8
The exponential model reaches convergence quickly because fixing
\(\sigma_t\) imposes a strong
constraint; the fixed parameter is reported as excluded from the
diagnostics. The generalized-gamma model has an additional shape
parameter q_t and needs more draws to converge. We now
compare the three models.
WAIC1 WAIC2 DIC
[1,] 2083.523 2083.659 2083.306
WAIC1 WAIC2 DIC
[1,] 2393.460 2393.552 2394.678
WAIC1 WAIC2 DIC
[1,] 2079.189 2079.437 2079.372
The exponential model has by far the highest (worst) information criteria, confirming that fixing \(\sigma_t\) at one is inappropriate here: the data were generated with \(\sigma_t = 0.2\), far from one. The Weibull and generalized-gamma models have very similar criteria, with only a small edge for the generalized gamma. Because the extra flexibility of the generalized gamma barely improves the fit, the more parsimonious Weibull is an adequate choice, which is plausible given that the data were generated from a Weibull model.
By default, get_ic uses every stored posterior draw
available after warm-up; it applies no additional thinning. The explicit
samples argument can request a random computational
subsample. Estimation is computationally expensive and scales with the
number of draws, so it is parallelized: cores sets the
number of CPUs, and cores = NULL uses all available
cores.
bayespimAfter estimating a model, interest usually lies in posterior
inference on the parameters and in cumulative incidence functions as a
function of time. BayesPIM provides built-in functionality
for both: parameter summaries (Section @ref(sec-parameter-summaries))
and posterior predictive CIFs (Section @ref(sec-cifs)).
The posterior parameter draws are stored in the fitted object as a
coda::mcmc.list named $par (Plummer et al.,
2006). With the default save_every = 1, it contains every
generated draw, including warm-up. With save_every > 1,
it contains only the selected storage iterations. The plotting method
discards stored draws belonging to the warm-up and may apply additional
plot-only thinning for rendering speed; it does not modify the fitted
object.
It can be helpful to thin the chains for faster plotting and to discard some additional warm-up. Here we show the trace and density plots for the incidence (latent-time) parameters after a thinning interval of five; the four chains are overlaid.
Trace and density plots for the incidence (latent-time) model parameters of the Weibull fit.
The chains mix well and are indistinguishable across the four colors,
consistent with the R-hat and ESS diagnostics. Posterior summaries are
obtained efficiently through the summary method, which
reports posterior medians, 95% credible intervals, and the convergence
diagnostics for each parameter block.
Latent-time distribution: weibull
Incidence sampler: slice_collapsed
Parameters of the latent-time model
2.5% 50% 97.5% R_hat ESS
beta_t_intercept 4.922 4.973 5.029 1.001 1275.2
beta_t_x_1 0.156 0.197 0.239 1.002 2508.5
beta_t_x_2 0.124 0.204 0.286 1.002 1651.9
sigma_t 0.177 0.207 0.241 1.001 1660.3
Parameters of the prevalence model
2.5% 50% 97.5% R_hat ESS
beta_g_intercept -0.986 -0.857 -0.728 1.002 1769.4
beta_g_x_1 0.058 0.145 0.236 1.001 2044.3
beta_g_x_2 0.093 0.273 0.452 1.002 1904.0
Convergence criteria: R-hat <= 1.010 and ESS >= 800.0
MCMC iterations generated: 8000 (2000 per chain)
Parameter draws stored: 8000 (2000 per chain; save_every = 1)
Warm-up cutoff: 500 generated iterations per chain
Stored warm-up draws omitted: 2000 (500 per chain)
Posterior draws used: 6000 (1500 per chain)
The estimates are close to the values used in the data generation:
the incidence intercept is near mu_t = 5, both incidence
slopes near beta_t = 0.2, and the AFT scale near
sigma_t = 0.2; the credible intervals cover the true
values. The prevalence slopes are likewise near
beta_g = 0.2, and the prevalence intercept near
qnorm(0.2) = -0.84. By default summary
discards the warm-up stored in the object; a different warm-up can be
supplied through the warmup argument for this summary
only.
For posterior means, additional quantiles, and Monte Carlo standard
errors, coda’s own summary method can be used on the
parameter chains. The helper trim_mcmc conveniently
discards warm-up and thins an mcmc.list.
Iterations = 501:2000
Thinning interval = 1
Number of chains = 4
Sample size per chain = 1500
1. Empirical mean and standard deviation for each variable,
plus standard error of the mean:
Mean SD Naive SE Time-series SE
beta_t_intercept 4.9735 0.02684 0.0003465 0.0007390
beta_t_x_1 0.1970 0.02129 0.0002748 0.0004249
beta_t_x_2 0.2037 0.04127 0.0005328 0.0010258
sigma_t 0.2078 0.01662 0.0002146 0.0004079
beta_g_intercept -0.8572 0.06593 0.0008511 0.0015724
beta_g_x_1 0.1451 0.04586 0.0005921 0.0010301
beta_g_x_2 0.2728 0.09145 0.0011806 0.0021003
2. Quantiles for each variable:
2.5% 25% 50% 75% 97.5%
beta_t_intercept 4.92248 4.9560 4.9728 4.9904 5.0290
beta_t_x_1 0.15619 0.1826 0.1967 0.2110 0.2394
beta_t_x_2 0.12380 0.1756 0.2038 0.2308 0.2857
sigma_t 0.17677 0.1963 0.2072 0.2187 0.2414
beta_g_intercept -0.98616 -0.9014 -0.8574 -0.8131 -0.7280
beta_g_x_1 0.05766 0.1137 0.1447 0.1771 0.2362
beta_g_x_2 0.09282 0.2127 0.2732 0.3315 0.4522
The generalized-gamma fit is summarized in the same way and
additionally reports the signed shape parameter q_t, for
which q_t = 1 recovers the Weibull and q_t = 0
the log-normal. Here q_t is estimated with considerable
posterior uncertainty, reflecting that interval-censored screening data
carry limited information about the precise distributional shape; this
is consistent with the small difference in information criteria between
the Weibull and generalized-gamma fits found in Section
@ref(sec-information-criteria).
A central question in screening research is the cumulative
probability of progression as a function of time. BayesPIM
estimates posterior predictive cumulative incidence functions (CIFs)
with the function ppCIF. As motivated in the Overview,
BayesPIM distinguishes two CIFs that handle prevalence
differently, and one ppCIF call computes both.
The non-prevalent CIF is the incidence function among individuals who are disease-free at baseline. It disentangles incidence from prevalence and equals the cumulative distribution function of the latent event time,
\[\begin{align} F_t(t_0 \mid \boldsymbol{\beta}_t, \sigma_t) = \Pr(t \le t_0 \mid \boldsymbol{\beta}_t, \sigma_t). \end{align}\]
Like an ordinary CIF, it starts at zero at time zero. The mixture CIF additionally represents prevalence as a point mass at time zero. Writing \(\theta\) for the prevalence probability,
\[\begin{align} F_{\mathrm{mix}}(t_0 \mid \cdot) = \theta + (1 - \theta)\,F_t(t_0 \mid \boldsymbol{\beta}_t, \sigma_t), \end{align}\]
so the mixture CIF starts at \(\theta > 0\) at time zero and then accumulates incident events among the non-prevalent fraction. The mixture CIF is the joint representation, useful when the total baseline-to-disease burden (prevalent plus incident) is of interest; the non-prevalent CIF isolates incidence.
Both CIFs come in a marginal and a
conditional variant. A marginal CIF integrates over the
empirical covariate distribution and describes a randomly selected
individual from the population. A conditional CIF fixes one or more
covariates and describes a specific subgroup (for example, individuals
with a given biomarker value). Conditionally, the prevalence probability
is \(\theta(\mathbf{x}_g) =
\Phi(\mathbf{x}_{g}'\boldsymbol{\beta}_g)\) and the incidence
CIF is \(F_t(t_0 \mid \mathbf{x}_t,
\boldsymbol{\beta}_t, \sigma_t)\); marginal CIFs average these
over the covariates. Because closed forms are not available for all
quantities, ppCIF obtains both CIFs by posterior predictive
simulation. It considers every stored post-warm-up draw and then
randomly selects the explicitly requested number
pst_samples; for each selected draw it simulates prevalence
indicators and event times and summarizes the results by the pointwise
posterior median and 95% credible band.
We distinguish two use cases: obtaining predictive probabilities at a few time points, and obtaining a full curve for plotting.
With ppd_type = "percentiles", ppCIF
returns cumulative probabilities at the times supplied in
quant. The example below evaluates both CIFs at 0, 100, and
200 time units.
set.seed(2025)
cif_pts <- ppCIF(mod_weibull, ppd_type = "percentiles", quant = c(0, 100, 200))
cif_pts$mixture$med_cdf # posterior median mixture CIF
cif_pts$mixture$med_cdf_ci # 2.5% and 97.5% posterior band
cif_pts$nonprevalent$med_cdf # posterior median non-prevalent CIF> cif_pts$mixture$med_cdf
[1] 0.240 0.362 0.897
> cif_pts$mixture$med_cdf_ci
[,1] [,2] [,3]
[1,] 0.204000 0.320 0.853
[2,] 0.276025 0.407 0.936
> cif_pts$nonprevalent$med_cdf
[1] 0.0000000 0.1596530 0.8646518
At time zero the mixture CIF equals 0.240 (95% CI
0.204–0.276), the estimated prevalence,
whereas the non-prevalent CIF is 0 by construction. By 100
time units the mixture CIF has risen to 0.362 and the
non-prevalent CIF to 0.160; by 200 time units both are near
0.9, as most events have occurred. The number of posterior
draws used is set by pst_samples (default
1000); increasing it reduces Monte Carlo error at some
computational cost.
Conditional probabilities are obtained through fix_x_t
and fix_x_g, each a numeric vector with one entry per
covariate. Numeric entries fix a covariate; NA entries are
integrated over. For example, fix_x_t = c(NA, 1)
marginalizes over the first (continuous) covariate and fixes the second
(discrete) covariate at one. When x_t and x_g
have the same number of columns, fix_x_t is also applied to
the prevalence model if fix_x_g is omitted.
For plotting, ppCIF is evaluated on a fine grid of times
and the resulting curve is drawn with plot.ppCIF. By
default the plot method shows the mixture CIF;
type = "nonprevalent" shows the non-prevalent CIF, and
type = "both" shows them side by side on a common time
scale.
set.seed(2026)
cif <- ppCIF(mod_weibull, pst_samples = 1e3, ppd_type = "percentiles",
quant = seq(0, 300, length.out = 601))
plot(cif, type = "both", xlim = c(0, 300))Posterior predictive mixture CIF (left) and non-prevalent CIF (right) with 95% credible bands.
The two panels make the role of prevalence explicit. The mixture CIF
starts at about 0.24, the prevalence mass, and rises to
one, whereas the non-prevalent CIF starts at zero and describes
incidence in the initially healthy sub-population. If quant
is left NULL, a default grid from zero to the maximum
finite follow-up time is used. Setting ci = FALSE omits the
credible bands.
Conditional CIFs are obtained in the same way, by fixing covariates
through fix_x_t and fix_x_g. For a compact
comparison, the median curves returned in
$nonprevalent$med_cdf (and the corresponding grid in
$quant) can be drawn together. The figure below overlays
the conditional non-prevalent CIFs for the two levels of the discrete
covariate, marginalizing over the continuous covariate.
set.seed(2027)
cif_x2_0 <- ppCIF(mod_weibull, fix_x_t = c(NA, 0), pst_samples = 1e3,
ppd_type = "percentiles", quant = seq(0, 300, length.out = 601))
set.seed(2028)
cif_x2_1 <- ppCIF(mod_weibull, fix_x_t = c(NA, 1), pst_samples = 1e3,
ppd_type = "percentiles", quant = seq(0, 300, length.out = 601))
plot(cif_x2_0, type = "nonprevalent", ci = FALSE, xlim = c(0, 300),
main = "Conditional non-prevalent CIF by baseline covariate")
lines(cif_x2_1$quant, cif_x2_1$nonprevalent$med_cdf, col = "#D55E00", lwd = 2)
legend("bottomright", bty = "n", lwd = 2, col = c("#0072B2", "#D55E00"),
legend = c("x2 = 0", "x2 = 1"))Conditional non-prevalent CIFs for the two levels of the discrete covariate, marginalizing over the continuous covariate.
Because the incidence slope of the discrete covariate is positive,
individuals with x2 = 1 reach a given cumulative incidence
later than individuals with x2 = 0, so their curve lies to
the right.
ppCIF also supports the inverse representation with
ppd_type = "quantiles", which returns event times at
cumulative probabilities supplied in perc (default a fine
grid on \([0,1]\)). This is convenient
when the quantities of interest are times-to-probability rather than
probabilities-at-time. The plotting method handles both representations
automatically.
This section discusses several further options that change the
behavior of bayespim.
So far the test sensitivity \(\kappa\) was fixed at a known value with
update_kappa = FALSE. When \(\kappa\) is unknown, it can be estimated by
setting update_kappa = TRUE and supplying an informative
Beta prior through kappa_prior = c(mean, sd). The prior
mean must lie strictly between zero and one, and the standard deviation
must satisfy \(0 < \mathrm{sd} <
\sqrt{\mathrm{mean}(1-\mathrm{mean})}\); the Beta shape
parameters are computed from these two moments. An informative prior is
advisable because sensitivity and incidence can be weakly identified
from screening data alone.
mod_kappa <- bayespim(
v_obs = dat$v_obs, x_t = dat$x, x_g = dat$x, r = dat$r,
update_kappa = TRUE,
kappa_prior = c(0.7, 0.1), # Beta prior with mean 0.7 and sd 0.1
ndraws = 1e3, warmup = 5e2, chains = 4, seed_chains = 1:4,
update_till_converge = TRUE, dist = "weibull"
)When \(\kappa\) is estimated, a
kappa block is added to the convergence diagnostics and to
summary. If kappa_prior = NULL, a warning is
issued and an uninformative \(\mathrm{Beta}(1,1)\) prior is used; we
generally advise against this default.
bayespim offers three samplers for the incidence
parameters, selected with sampler. The default
"slice_collapsed" augments only the latent screening
interval and updates the parameters from the interval-censored
likelihood, as described in Section @ref(sec-gibbs). The alternative
"slice" augments the exact latent event times and then
updates the parameters by univariate slice sampling, and
"mh" augments the exact times and updates the parameters
with a random-walk Metropolis step, as in the original implementation of
Klausch et al. (2026). The two exact-time samplers are retained mainly
for comparison. The collapsed sampler is the default because collapsing
over the exact times reduces MCMC autocorrelation and speeds up
convergence, and because the generalized-gamma model is supported only
with the collapsed sampler.
Unlike the slice samplers, the Metropolis sampler
(sampler = "mh") requires a tuning parameter: the standard
deviation prop_sd of the normal random-walk proposal (\(\sigma_t\) is proposed on the log scale). A
good prop_sd yields an acceptance rate near 23%, which is
often quoted as efficient for random-walk Metropolis under approximate
normality (Roberts et al., 1997). A suitable value is not known a priori
and has to be tuned; in our experience it usually lies between 0.001 and
0.1.
The helper search_prop_sd automates this search. It
takes a short initial Metropolis fit and adjusts prop_sd
with a heuristic rule until the acceptance rate falls within
acc_bounds (default c(0.2, 0.25)), doubling
the number of draws succ_min times (default 3)
to confirm stability.
mod_mh_ini <- bayespim(
v_obs = dat$v_obs, x_t = dat$x, x_g = dat$x, r = dat$r,
kappa = 0.7, update_kappa = FALSE,
ndraws = 1e3, warmup = 5e2, chains = 4, seed_chains = 1:4,
sampler = "mh", prop_sd = 0.005, dist = "weibull"
)
search_sd <- search_prop_sd(m = mod_mh_ini)
search_sd$prop_sdIteration 1
Acceptance rate was: 0.746
prop_sd is set to 0.015
...
Success. Doubling number of MCMC draws: 4000
Finished calibrating proposal variance.
> search_sd$prop_sd
[1] 0.01590472
The calibrated prop_sd can then be passed to a full
Metropolis run. In this example the acceptance rate is brought close to
the target, but the incidence parameters still mix slowly under
Metropolis sampling, needing many more draws than the collapsed slice
sampler to reach comparable effective sample sizes. This illustrates why
the collapsed slice sampler is the default; the Metropolis sampler is
mainly of interest for reproducing the original method.
The slice samplers have their own tuning parameter, the initial
bracket width used in the step-out procedure (Neal, 2003), controlled by
slice_width (default 1). It does not affect
the target posterior, only computational efficiency. If the width is too
small, the sampler may need many step-out steps to bracket the slice; if
too large, it may spend more time shrinking the bracket. The default
works well in most applications; if the slice sampler mixes slowly or is
unexpectedly slow, adjusting slice_width can help.
By default bayespim uses the weakly informative priors
of Section @ref(sec-prior-assumptions) through its internal log-prior
function log_aft_prior. Users can supply a custom log-prior
for the incidence parameters through log_prior_fun. The
function must accept the named arguments eta,
dist, beta_prior, tau_t,
sig_prior, and q_prior_sd, and return a single
numeric log-density. Here eta is the vector of incidence
coefficients with log(sigma_t) appended in the last
position (and the signed shape Q after it for
dist = "gengamma"). When
standardize_covariates = TRUE, this function receives the
internally standardized incidence coefficients, so a custom prior is
also defined on that scale. See ?log_aft_prior for the
exact contract.
The scale of the screening times passed to bayespim is
often arbitrary (days, weeks, months, or years). Changing the time scale
mainly shifts the AFT intercept \(\beta_{t0}\), which is regularized by its
prior, so the amount of regularization would otherwise depend on the
chosen units. By default (rescale_times = TRUE),
bayespim rescales the screening times internally by the
median finite observation time before fitting, and restores the returned
times and incidence intercept to the original scale afterward. Dividing
times by a constant \(c\) shifts the
intercept by \(-\log(c)\), which is
added back after fitting; the slopes and scale parameter are unaffected.
This keeps the default prior on the intercept meaningful regardless of
the time unit. Rescaling can be turned off with
rescale_times = FALSE, in which case care should be taken
with the intercept prior.
Covariate standardization is another affine reparameterization. For a continuous column \(x_j\), let \(z_j=(x_j-m_j)/s_j\), where \(m_j\) and \(s_j\) are its sample mean and standard deviation. If \(\widetilde\beta_j\) is sampled as the coefficient of \(z_j\), the coefficient per original covariate unit is
\[ \beta_j=\frac{\widetilde\beta_j}{s_j}. \]
For both the AFT and probit prevalence components, the original-scale intercept is obtained by subtracting \(\sum_j m_j\beta_j\) from the internally sampled intercept. The AFT intercept additionally receives the \(+\log(c)\) time-scale correction described above. Thus
\[ \beta_{t0}=\widetilde\beta_{t0}+\log(c)-\sum_jm_{tj}\beta_{tj}, \qquad \beta_{g0}=\widetilde\beta_{g0}-\sum_jm_{gj}\beta_{gj}. \]
Every posterior draw is transformed before it is returned, so
credible intervals and parameter dependence are preserved. Scale/shape
parameters such as \(\sigma_t\) and
generalized-gamma \(Q\), and test
sensitivity \(\kappa\), are unaffected.
Information-criterion and posterior-predictive calculations reconstruct
the centered parameterization internally and use standardized covariates
for numerical stability, while fixed covariate values supplied to
ppCIF remain on the original scale. The fitted centers and
standard deviations are stored in $covariate_scaling and
inherited unchanged by model updates.
Bürkner, P.-C., Gabry, J., Kay, M., & Vehtari, A. (2026). posterior: Tools for working with posterior distributions. R package version 1.7.0.
Gelman, A., Hwang, J., & Vehtari, A. (2014). Understanding predictive information criteria for Bayesian models. Statistics and Computing, 24(6), 997–1016. https://doi.org/10.1007/s11222-013-9416-2
Klausch, T., Lissenberg-Witte, B. I., & Coupé, V. M. H. (2026). A Bayesian prevalence-incidence mixture model for screening outcomes with misclassification. Statistics in Medicine, 45(8–9), e70433. https://doi.org/10.1002/sim.70433
Liu, J. S. (1994). The collapsed Gibbs sampler in Bayesian computations with applications to a gene regulation problem. Journal of the American Statistical Association, 89(427), 958–966. https://doi.org/10.1080/01621459.1994.10476829
Neal, R. M. (2003). Slice sampling. The Annals of Statistics, 31(3), 705–767. https://doi.org/10.1214/aos/1056562461
Plummer, M., Best, N., Cowles, K., & Vines, K. (2006). CODA: Convergence diagnosis and output analysis for MCMC. R News, 6(1), 7–11.
Roberts, G. O., Gelman, A., & Gilks, W. R. (1997). Weak convergence and optimal scaling of random walk Metropolis algorithms. The Annals of Applied Probability, 7(1), 110–120. https://doi.org/10.1214/aoap/1034625254
Spiegelhalter, D. J., Best, N. G., Carlin, B. P., & van der Linde, A. (2002). Bayesian measures of model complexity and fit. Journal of the Royal Statistical Society: Series B, 64(4), 583–639. https://doi.org/10.1111/1467-9868.00353
Vehtari, A., Gelman, A., Simpson, D., Carpenter, B., & Bürkner, P.-C. (2021). Rank-normalization, folding, and localization: An improved R-hat for assessing convergence of MCMC. Bayesian Analysis, 16(2), 667–718. https://doi.org/10.1214/20-BA1221
Watanabe, S. (2010). Asymptotic equivalence of Bayes cross validation and widely applicable information criterion in singular learning theory. Journal of Machine Learning Research, 11, 3571–3594.