Package {exnexSurv}


Type: Package
Title: Bayesian EXNEX Models for Survival Analysis in Basket Trials
Version: 1.3.0
Description: Implements the Bayesian Exchangeable Non-Exchangeable (EXNEX) framework for right-censored log-normal survival data in basket trials. Based on 'Rcpp' and 'RcppArmadillo', the package provides a fast Gibbs sampler supporting EXNEX, complete pooling, and no pooling models to facilitate methodological comparisons and simulation studies.
License: MIT + file LICENSE
URL: https://github.com/victorney/exnexSurv, https://victorney.github.io/exnexSurv/
BugReports: https://github.com/victorney/exnexSurv/issues
Imports: Rcpp (≥ 1.1.1), hardhat (≥ 1.4.0), checkmate (≥ 2.1.0)
Suggests: testthat (≥ 3.3.0), survival (≥ 3.8), knitr, rmarkdown, ggplot2, bayesplot, viridisLite
VignetteBuilder: knitr
LinkingTo: Rcpp, RcppArmadillo
Encoding: UTF-8
Config/roxygen2/version: 8.1.0
NeedsCompilation: yes
Packaged: 2026-08-30 15:53:41 UTC; victo
Author: Victor Ney [aut, cre]
Maintainer: Victor Ney <victorney@ime.usp.br>
Repository: CRAN
Date/Publication: 2026-09-10 14:20:15 UTC

exnexSurv: Bayesian EXNEX Models for Survival Analysis in Basket Trials

Description

Fits Bayesian Exchangeable–Non-Exchangeable (EXNEX) survival models for right-censored log-normal data in basket trials using a fast, data-augmented Gibbs sampler written in C++ (Rcpp/RcppArmadillo).

For patient i, \log T_i=\theta_{g[i]}+X_i^{\mathsf T}\beta+ \varepsilon_i with \varepsilon_i\sim\mathcal N(0,\sigma^2). A latent indicator Z_j selects, for each basket j, whether its effect is drawn from the exchangeable component \mathcal N(\mu,\tau^2) or from a basket-specific non-exchangeable prior \mathcal N(m_{0j},v_{0j}). Censored event times are handled by data augmentation (imputing truncated-Normal latent log-times), which makes every full conditional conjugate and the systematic Gibbs scan exact.

The main entry point is exnex_surv(). See the package vignette The EXNEX Model, Priors, and Data Augmentation for a full presentation of the model, the priors, and the sampler.

Author(s)

Maintainer: Victor Ney victorney@ime.usp.br

Authors:

References

Neuenschwander, B., Wandel, S., Roychoudhury, S., & Bailey, S. (2016). Robust exchangeability designs for early phase clinical trials with multiple strata. Pharmaceutical Statistics, 15(2), 123–134.

Tanner, M. A., & Wong, W. H. (1987). The calculation of posterior distributions by data augmentation. Journal of the American Statistical Association, 82(398), 528–540.

See Also

exnex_surv, summary.exnex_surv, print.exnex_surv, plot.exnex_surv


Extract event vector from outcomes

Description

Extract event vector from outcomes

Usage

.extract_event_vector(outcomes)

Extract time vector from outcomes

Description

Extract time vector from outcomes

Usage

.extract_time_vector(outcomes)

Derive one deterministic seed per chain (restores the global RNG state)

Description

Derive one deterministic seed per chain (restores the global RNG state)

Usage

.make_chain_seeds(chains, seed = NULL)

Run multiple chains in parallel via PSOCK workers

Description

Run multiple chains in parallel via PSOCK workers

Usage

.run_chains_parallel_exnex(
  cpp_data,
  priors,
  iter,
  warmup,
  chains,
  parallel_chains,
  chain_seeds
)

Run a single chain by calling the C++ kernel once

Description

Run a single chain by calling the C++ kernel once

Usage

.run_single_chain_exnex(cpp_data, priors, iter, warmup, seed)

Compare multiple exnex_surv fits by WAIC

Description

Computes [compute_waic()] for each supplied fit and reports them in a single 'data.frame', sorted by ascending WAIC.

Usage

compare_waic(..., digit = 2)

Arguments

...

Two or more fitted 'exnex_surv' objects.

digit

Number of decimal places for the reported statistics.

Value

A 'data.frame' with one row per model and columns 'model', 'waic', 'se_elpd_waic', 'lpd', 'p_waic', 'elpd_waic'.


Pointwise log-likelihood and WAIC from an exnex_surv fit

Description

Computes the Watanabe-Akaike Information Criterion (WAIC) for a fitted 'exnex_surv' model. WAIC is a fully Bayesian information criterion that uses the posterior draws of the log-likelihood and adds a penalty for effective number of parameters p_{waic}.

Usage

compute_waic(fit, ...)

Arguments

fit

A fitted 'exnex_surv' object.

...

Unused.

Details

For each observation i, with posterior draws indexed by s=1,\ldots,S, we compute the pointwise log-likelihood \log p(y_i \mid \theta^{(s)}), where the log-normal AFT model gives for an observed event (\delta_i=1)

\log p(y_i \mid \theta^{(s)}) = \log\phi\!\left(\frac{\log t_i-\eta_i^{(s)}}{\sigma^{(s)}}\right) - \log(\sigma^{(s)} t_i),

and for a censored observation (\delta_i=0)

\log p(y_i \mid \theta^{(s)}) = \log\!\left[1-\Phi\!\left(\frac{\log t_i-\eta_i^{(s)}}{\sigma^{(s)}}\right)\right],

where \eta_i^{(s)} is the linear predictor built from the draws.

WAIC is then

lpd = \sum_i \log\!\left(\frac{1}{S}\sum_s \exp\log p(y_i\mid\theta^{(s)})\right),

p_{waic} = \sum_i \mathrm{var}_s\big(\log p(y_i\mid\theta^{(s)})\big),

\mathrm{WAIC} = -2(lpd - p_{waic}).

Rows with extreme leverage ('p_waic' above a large threshold) may flag issues; the function returns a 'pointwise' matrix so users can investigate.

Value

A named list with elements 'waic', 'se_elpd_waic', 'lpd', 'p_waic', 'elpd_waic', and 'pointwise' (an 'n' by 3 matrix with columns 'lpd', 'p_waic', 'waic').


Main Gibbs Sampler for EXNEX Survival Models

Description

Implements Gibbs sampling for Bayesian EXNEX right-censored log-normal survival models with optional covariates.

Usage

cpp_exnex_gibbs(time, event, group, X, priors, iter, warmup, chains)

Arguments

time

Vector of observed follow-up times (n-vector, all > 0)

event

Vector of event indicators (n-vector, 0 or 1)

group

Vector of group assignments (n-vector, integers 1 to K)

X

Matrix of covariates (n x P). Can be empty (n x 0) if no covariates.

priors

Optional named list of prior hyperparameters. Supported fields: a_sigma, b_sigma, a_tau, b_tau (inverse-Gamma shape and scale), p_mix (EXNEX mixture weight), m_mu, v_mu (exchangeable mean prior), m_nex, v_nex (nonexchangeable component), v_beta (variance of the regression-coefficient prior). p_mix, m_nex, and v_nex each accept either a scalar, replicated across baskets, or a numeric vector of length K with one value per basket, matching the basket-specific notation p_{\mathrm{exch},j}, m_{0j}, v_{0j} of the model. Absent fields keep the defaults; unknown fields are ignored.

iter

Total number of MCMC iterations

warmup

Number of iterations to discard

chains

Number of independent chains to run

Value

List containing posterior draws, priors, metadata, and diagnostics


Fit Bayesian EXNEX Survival Models

Description

Fits a Bayesian Exchangeable Non-Exchangeable (EXNEX) hierarchical model for right-censored log-normal survival data in basket trials using a data-augmented Gibbs sampler.

Usage

exnex_surv(x, ...)

## Default S3 method:
exnex_surv(x, ...)

## S3 method for class 'formula'
exnex_surv(
  formula,
  data,
  priors = list(),
  iter = 2000,
  warmup = 1000,
  chains = 1,
  parallel_chains = 1,
  group_col = NULL,
  seed = NULL,
  ...
)

## S3 method for class 'data.frame'
exnex_surv(
  x,
  y,
  priors = list(),
  iter = 2000,
  warmup = 1000,
  chains = 1,
  parallel_chains = 1,
  group_col = NULL,
  seed = NULL,
  ...
)

Arguments

x

An object containing the predictors (subgroup assignment). Can be a data frame or a formula.

...

Additional arguments passed to methods.

formula

A model formula with structure: 'Surv(time, event) ~ group + covariates'. The first RHS variable is the group/basket assignment. Additional variables are covariates.

data

A data frame containing the variables in the formula.

priors

Optional named list of prior hyperparameters. Supported fields: a_sigma, b_sigma, a_tau, b_tau (inverse-Gamma shape and scale for the residual variance \sigma^2 and the between-basket variance \tau^2); p_mix (EXNEX mixture weight, strictly between 0 and 1); m_mu, v_mu (prior mean and variance of the exchangeable center \mu); m_nex, v_nex (prior mean and variance of the nonexchangeable component); v_beta (variance of the regression-coefficient prior). p_mix, m_nex, and v_nex each accept either a scalar, replicated across baskets, or a numeric vector of length K with one value per basket, matching the basket-specific notation of the model. All other fields are scalars. Absent fields keep the defaults (inverse-Gamma(2,2) for the variances, p_mix = 0.5, m_mu = 0, v_mu = 1e4, m_nex = 0, v_nex = 1e4, v_beta = 1e4); unknown fields are ignored.

iter

Total number of MCMC iterations. Default is 2000.

warmup

Number of warmup iterations to discard. Default is 1000. Posterior samples will have (iter - warmup) rows.

chains

Number of independent MCMC chains. Default is 1.

parallel_chains

Number of chains to run in parallel at the R level. Must be between 1 and 'chains'. Default is 1 (sequential chain execution).

group_col

Name of the column in 'x' that represents the basket/group assignment. If NULL (default), assumes the first column in x is the group.

seed

Random seed for reproducibility (optional).

y

A Surv object or matrix containing outcome (time and event status).

Details

For patient i, the model is

\log T_i = \theta_{g[i]} + X_i^{\mathsf T}\beta + \varepsilon_i,\quad \varepsilon_i\sim\mathcal N(0,\sigma^2),

with a latent EXNEX hierarchy on the basket effects,

\theta_j\mid Z_j\sim Z_j\,\mathcal N(\mu,\tau^2) +(1-Z_j)\,\mathcal N(m_{0j},v_{0j}),\quad Z_j\sim\mathrm{Bern}(p_{\mathrm{exch},j}),

so that each basket either borrows strength from the exchangeable component \mathcal N(\mu,\tau^2) or follows its own non-exchangeable prior \mathcal N(m_{0j},v_{0j}). The first predictor variable in the model formula is interpreted as the basket/group assignment; additional variables are covariates in the linear predictor on the log-survival scale.

Censored event times are handled by data augmentation: a censored log-time is imputed from its truncated-Normal conditional distribution (inverse-CDF in log space) before the remaining parameters are updated with their conjugate full conditionals. The sampler keeps only the posterior draws of \theta_j, \beta, and \sigma^2.

Value

An object of class 'exnex_surv' containing posterior samples and metadata. Components include draws (a data frame with columns theta_1, ..., theta_K, beta_1, ..., beta_P, and sigma2), data (the processed data: time, event, group, X, n, n_groups, n_covariates, cov_names, chain_seeds), priors (the supplied priors), resolved_priors (defaults merged with overrides), iter, warmup, chains, and blueprint. Use summary(), print(), and plot() to inspect it.

Examples

# Small simulated dataset: three baskets, one covariate, ~25% censoring
set.seed(1)
n <- 90
group <- factor(rep(1:3, each = 30))
x1 <- rnorm(n)
eta <- rep(c(1.1, 1.6, 2.0), each = 30) + 0.5 * x1
true_time <- exp(eta + rnorm(n, 0, 0.6))
cens_time <- runif(n, 2, 9)
d <- data.frame(
  time = pmin(true_time, cens_time),
  event = as.integer(true_time <= cens_time),
  group = group,
  x1 = x1
)

# Fit with group effects and one covariate (small run for the example)
fit <- exnex_surv(
  survival::Surv(time, event) ~ group + x1,
  data = d,
  priors = list(p_mix = 0.7),
  iter = 300, warmup = 150, chains = 1
)
print(fit, show_trace = FALSE)
summary(fit)


Bridge connecting hardhat processed data to the C++ Gibbs Sampler

Description

Orchestrates data preparation, validation, and passes it to the C++ MCMC engine.

Usage

exnex_surv_bridge(
  processed,
  priors,
  iter,
  warmup,
  chains,
  parallel_chains,
  group_col,
  original_data,
  seed = NULL
)

Arguments

processed

A list produced by 'hardhat::mold()'.

priors

Optional named list of prior hyperparameters; see exnex_surv().

iter

Total number of MCMC iterations.

warmup

Number of warmup iterations.

chains

Number of chains to run.

parallel_chains

Number of chains to run in parallel at the R level.

group_col

Name of the original group column.

original_data

The original data frame (before hardhat processing).

seed

Optional seed used to deterministically derive one seed per chain.

Details

The group variable (specified by group_col) is identified from the original data. Additional variables are treated as covariates in the linear predictor.

Value

An 'exnex_surv' object.


Posterior median survival time

Description

For the log-normal AFT model the median survival time for a linear predictor \eta is t_{med} = \exp(\eta), since S(t)=0.5 when \log t = \eta. Posterior draws of \theta and \beta therefore induce a posterior distribution of t_{med} whose quantiles are reported.

Usage

median_survival(fit, newdata = NULL, level = 0.95, ...)

Arguments

fit

A fitted 'exnex_surv' object.

newdata

Optional data frame (one row gives one median).

level

Credible-interval level (default '0.95').

...

Unused.

Value

A 'data.frame' with columns 'group', 'median', 'lower', 'upper'.


Constructor for exnex_surv Objects

Description

Creates a formal S3 object containing posterior samples and metadata from EXNEX model fitting.

Usage

new_exnex_surv(
  draws,
  data,
  priors,
  resolved_priors = priors,
  iter,
  warmup,
  chains,
  blueprint
)

Arguments

draws

A data frame containing posterior samples. Columns are: theta_1, ..., theta_K, beta_1, ..., beta_P (if P > 0), sigma2.

data

A list containing: - time: Observed follow-up times - event: Event indicators (0/1) - group: Group assignments - X: Covariate matrix (can be empty) - n: Total number of observations - n_groups: Number of groups - n_covariates: Number of covariates - cov_names: Covariate column names

priors

A list of prior specifications used for fitting.

resolved_priors

A named list of the prior hyperparameters actually used (defaults merged with any overrides supplied in priors).

iter

Total MCMC iterations performed.

warmup

Number of warmup iterations discarded.

chains

Number of chains run.

blueprint

The hardhat blueprint for the original formula/data structure.

Value

An 'exnex_surv' object (S3 class).


Plot parameter traces from an exnex_surv fit

Description

Generates one bayesplot traceplot per parameter.

Usage

## S3 method for class 'exnex_surv'
plot(x, parameters = NULL, ask = interactive(), ...)

Arguments

x

A fitted 'exnex_surv' object.

parameters

Optional character vector of parameter names to plot.

ask

Should R pause between plots? Defaults to 'interactive()'.

...

Unused.

Value

Invisibly returns 'x'.


Plot survival curves from an exnex_surv fit

Description

Draws the posterior median and credible band of the survival function for each group. Requires 'ggplot2'.

Usage

## S3 method for class 'survival_exnex'
plot(x, ...)

Arguments

x

An object of class 'survival_exnex' returned by [survival_curves()].

...

Unused.

Value

Invisibly returns 'x'.


Print an exnex_surv fit

Description

Prints a compact summary and optionally traceplots of posterior draws.

Usage

## S3 method for class 'exnex_surv'
print(x, show_trace = TRUE, parameters = NULL, max_parameters = Inf, ...)

Arguments

x

A fitted 'exnex_surv' object.

show_trace

Should traceplots be shown? Default is 'TRUE'.

parameters

Optional character vector of parameter names for traceplots.

max_parameters

Maximum number of parameters to plot.

...

Unused.

Value

Invisibly returns 'x'.


Posterior probability that one group beats another

Description

Estimates the posterior probability that a certain scalar summary of the survival distribution for group 'a' is greater than the same summary for group 'b'. Supported summaries are the median survival time, the survival probability at a fixed time 'S(t_0)', and the restricted mean survival time (RMST). The function works draw-by-draw on the posterior, so the probability is computed on the joint posterior of the two groups.

Usage

probability_superiority(
  fit,
  a,
  b,
  function_of = "median",
  newdata = NULL,
  times = NULL,
  tmax = NULL,
  ...
)

Arguments

fit

A fitted 'exnex_surv' object.

a

An integer index (or group label) of the first group.

b

An integer index (or group label) of the second group.

function_of

Character; one of '"median"', '"survival"' (alias '"S_t"'), or '"rmst"'. Selects which summary is compared.

newdata

Optional data frame with one row per group, used to fix covariates. If 'NULL', covariates are set to zero.

times

A scalar time at which to evaluate the survival function when 'function_of = "survival"'.

tmax

A positive horizon when 'function_of = "rmst"'.

...

Unused.

Value

A named list with elements 'prob' (posterior probability that the summary of group 'a' exceeds that of group 'b'), 'summary' (which summary was used), and 'groups' (the two groups compared).


Restricted mean survival time (RMST) from an exnex_surv fit

Description

Computes the restricted mean survival time up to a horizon 'tmax':

RMST(tmax) = \int_0^{tmax} S(t)\,dt.

The integral is evaluated numerically (trapezoidal rule over a fine grid) for every posterior draw, then summarised with posterior quantiles.

Usage

rmst(fit, tmax = NULL, newdata = NULL, level = 0.95, grid_points = 400, ...)

Arguments

fit

A fitted 'exnex_surv' object.

tmax

A positive scalar horizon up to which the RMST is computed. Defaults to 'max(fit$data$time)'.

newdata

Optional data frame (one row gives one RMST).

level

Credible-interval level (default '0.95').

grid_points

Resolution of the numerical quadrature (default '400').

...

Unused.

Value

A 'data.frame' with columns 'group', 'rmst', 'lower', 'upper'.


Simulate basket-trial log-normal survival data

Description

Generates a synthetic dataset from the log-normal AFT model used by 'exnexSurv':

\log T_i = \theta_{g[i]} + X_i^\top\beta + \varepsilon,\qquad \varepsilon\sim\mathcal N(0,\sigma^2).

Healthy baskets draw their location from a common baseline plus random noise; "outlier" baskets (specified via 'outlier_baskets') have their location shifted by 'resist_delta'. The function is useful for simulation studies and teaching examples.

Usage

simulate_data(
  n = 30,
  K = 9,
  beta = c(0.5, -0.2),
  sigma = 1.2,
  outlier_baskets = NULL,
  resist_delta = -0.8,
  censoring_rate = NULL,
  censor_upper = NULL,
  theta = 0,
  seed = NULL
)

Arguments

n

Number of patients per basket. A scalar is replicated across all 'K' baskets; a numeric vector of length 'K' assigns a size to each basket individually.

K

Number of baskets (default '9').

beta

Numeric vector of regression coefficients for the covariates. Length determines the number of covariates.

sigma

Residual standard deviation (default '1.2').

outlier_baskets

Optional integer vector of basket indices (1 to K) whose true location is shifted away from the healthy population. These are the baskets the EXNEX model is designed to detect.

resist_delta

Additive shift applied to the 'theta' of outlier baskets (default '-0.8').

censoring_rate

Approximate proportion of censoring after 'censor_upper'; if 'NULL', no censoring is applied.

censor_upper

Upper bound of the censoring-time uniform distribution.

theta

Baseline location for the healthy (non-outlier) baskets; a scalar used as the centre around which the healthy basket locations vary.

seed

Optional seed for reproducibility.

Value

A 'data.frame' with columns 'time', 'event', 'group' (a factor), and one covariate column ('x1', ...) per entry in 'beta'. True parameter values are stored as attributes 'true_theta', 'true_beta', and 'true_sigma'.


Summarize posterior draws from an exnex_surv fit

Description

Summarize posterior draws from an exnex_surv fit

Usage

## S3 method for class 'exnex_surv'
summary(object, probs = c(0.05, 0.5, 0.95), ...)

Arguments

object

A fitted 'exnex_surv' object.

probs

Quantiles to report.

...

Unused.

Value

A data frame with posterior summaries.


Survival curves from an exnex_surv fit

Description

Computes posterior survival curves S(t) = \Pr(T > t) from a fitted 'exnex_surv' model for a log-normal AFT specification:

\log T_i = \theta_{g[i]} + X_i^\top\beta + \varepsilon,\qquad \varepsilon\sim\mathcal N(0,\sigma^2).

For a fixed time t and linear predictor \eta, the survival probability of the log-normal distribution is

S(t) = 1 - \Phi\!\left(\frac{\log t - \eta}{\sigma}\right),

where \sigma^2 is the residual variance. Posterior draws of (\theta, \beta, \sigma^2) are propagated through this expression to obtain a full posterior distribution of S(t) at each time point.

Usage

survival_curves(fit, newdata = NULL, times = NULL, level = 0.95, ...)

Arguments

fit

A fitted 'exnex_surv' object.

newdata

Optional data frame with columns matching the covariates of the model. If it contains a 'group' column, that is used for the group index; otherwise the first group is used for all rows.

times

Optional numeric vector of times at which to evaluate the curves. If 'NULL', a sensible grid is built from the observed follow-up times.

level

Credible-interval level (default '0.95').

...

Unused.

Details

If 'newdata' is supplied, each row is evaluated at its own group and covariate values; otherwise the covariates are fixed at zero and the first group is used (with a warning if more than one group exists).

Value

An object of class 'survival_exnex' (also a 'data.frame') with long format columns: 'time', 'median', 'lower', 'upper', and 'group'.


Shared validation for common exnex_surv arguments

Description

Shared validation for common exnex_surv arguments

Usage

validate_args(priors, iter, warmup, chains, parallel_chains, group_col, seed)