bayprior bayprior logo

CRAN status R CMD check codecov Live App

Structured Bayesian prior elicitation, conflict diagnostics, and regulatory reporting.


Overview

bayprior is an advanced interactive R package and Shiny application designed for biostatisticians and clinical researchers to implement Bayesian Prior Elicitation, Conflict Diagnostics, and Sensitivity Analysis for clinical trials.

bayprior focuses on a specific stage of the Bayesian trial workflow: how do you construct, validate, and justify your prior to a regulator? It brings structured elicitation, prior-data conflict diagnostics, and regulatory report generation together in one integrated workflow. The FDA’s 2026 draft guidance on Bayesian methods makes this a growing area of focus.

bayprior enables users to:


Features and Modules

Module Detail Primary Output Goal
Prior Elicitation Quantile matching, moment matching, SHELF roulette for Beta / Normal / Gamma / Log-Normal / Exponential / Weibull Fitted density plot + parameter table Structured expert prior elicitation
Expert Pooling Linear and logarithmic opinion pooling with support compatibility validation Consensus density overlay + Bhattacharyya matrix Aggregate multi-expert beliefs
MAP Prior (Historical) Meta-analytic-predictive prior via random-effects meta-analysis, with an explicit prior on between-trial heterogeneity (tau) Fitted prior + tau posterior plot Derive an informative prior from historical trials
Conflict Diagnostics Box p-value (Normal approximation or exact prior predictive), S-value (surprisal), surprise index, KL divergence, Bhattacharyya overlap; binary, continuous, Poisson, and survival data Prior-Likelihood-Posterior overlay Detect prior misspecification
Mahalanobis Check Two-endpoint multivariate conflict test Chi-sq p-value + per-parameter z-scores Co-primary endpoint trials
Sensitivity Analysis Hyperparameter grid over posterior mean, SD, CrI width, Pr(efficacy); independent data entry Tornado plot + influence heatmap Demonstrate robustness to regulators
Sceptical Prior Spiegelhalter-Freedman centred-at-null prior Prior density + summary statistics Conservative regulatory sensitivity
Robust Mixture Schmidli et al. MAP robust mixture prior Robust vs informative density overlay Protection against misspecification
Power Prior Ibrahim-Chen calibrated borrowing weight via Bayes Factor Calibration curves + optimal delta Principled historical data borrowing
Export Report HTML / PDF / Word (.docx) prior justification document via Quarto Regulatory-ready self-contained report Submission documentation

Core Methodology

Prior Elicitation

Three structured elicitation approaches are implemented. Quantile matching fits a parametric distribution to expert-specified probability-value pairs via numerical optimisation. Moment matching derives hyperparameters analytically from an expert-supplied mean and SD. The SHELF roulette method (Oakley & O’Hagan, 2010) lets the expert allocate chips across histogram bins, fitting a parametric family to the chip allocation in real time.

All three methods support six distribution families:

Family Support Typical use
Beta (0, 1) Response rates, proportions
Normal (-Inf, Inf) Mean differences, log odds ratios
Gamma (0, Inf) Event rates, variances, survival times
Log-Normal (0, Inf) Hazard ratios, PK parameters
Exponential (0, Inf) Constant hazard rates, Poisson rate priors
Weibull (0, Inf) Non-constant hazard survival times (OS, PFS)

MAP Prior (Historical Data)

The meta-analytic-predictive (MAP) approach (Schmidli et al., 2014) derives an informative prior directly from multiple historical trials via a random-effects meta-analysis, with an explicit prior on the between-trial heterogeneity parameter tau – how much heterogeneity is assumed among historical trials directly affects how much borrowing is justified, so map_prior() requires this to be stated rather than left as an unstated default (see ?resolve_tau_prior for outcome-specific defaults). It returns an ordinary prior object that can be used directly, or passed into robust_prior() for an additional vague-mixture component.

y/se per historical trial can be supplied directly, or derived from raw trial-level summary statistics via the optional convenience function historical_effect_sizes(), a thin wrapper around metafor::escalc() (metafor is Suggests-only and not required to use map_prior() itself).

Prior-Data Conflict Diagnostics

Conflict detection follows Box (1980). Complementary metrics are computed:

By default the Box p-value uses a Normal approximation to the prior and the likelihood. prior_conflict(..., exact = TRUE) instead uses the exact prior predictive distribution where one has a closed form: Beta-Binomial (Beta prior, binary data) and Gamma-Poisson / Negative Binomial (Gamma prior, Poisson or survival data). Other combinations fall back to the Normal approximation with a message, and $box_pvalue_method reports which was used.

Four data types are supported:

Data type Conjugate update Typical endpoint
Binary (x events / n) Beta-Binomial Response rate, ORR
Continuous (mean, SD, n) Normal-Normal Mean difference
Poisson / count (events / exposure) Gamma-Poisson Adverse event rate
Survival (events / follow-up time) Gamma-Exponential Hazard rate, OS, PFS

Sensitivity Analysis

The sensitivity module is fully independent of conflict diagnostics — users can enter observed data directly without running conflict diagnostics first. Results are visualised as tornado plots and influence heatmaps. A dedicated sensitivity_cri() function tracks credible interval width specifically — a key regulatory quantity.

Robust and Power Priors

The robust mixture prior (Schmidli et al., 2014) mixes the informative prior with a vague Normal component. The sceptical prior (Spiegelhalter & Freedman, 1994) is centred at the null treatment effect. The power prior (Ibrahim & Chen, 2000) down-weights historical data by delta in (0, 1], calibrated to achieve a target Bayes Factor.


Shiny application

run_app() launches the full workflow as an interactive app (also available as a live demo). The Welcome page shows the analytical workflow, tracks which steps you have completed, and can load a synthetic Phase II example (TRIAL-001) so you can try the conflict check straight away.


Installation

Type Source Command
Release CRAN install.packages("bayprior")
Development GitHub pak::pak("ndohpenngit/bayprior")

PDF reports require Quarto CLI and a LaTeX installation:

install.packages("tinytex")
tinytex::install_tinytex()

Quick Start

library(bayprior)

# Elicit a Beta prior
prior <- elicit_beta(mean = 0.35, sd = 0.10, method = "moments",
                     label = "Response rate", expert_id = "Expert_1")
plot(prior)

# Pool two experts
e1  <- elicit_beta(mean = 0.30, sd = 0.10, method = "moments", expert_id = "E1")
e2  <- elicit_beta(mean = 0.42, sd = 0.12, method = "moments", expert_id = "E2")
agg <- aggregate_experts(list(E1 = e1, E2 = e2), weights = c(0.6, 0.4))

# Conflict diagnostics (add exact = TRUE for the exact prior predictive p-value)
cd <- prior_conflict(prior, list(type = "binary", x = 18, n = 40))
print(cd)

# Derive a MAP prior from historical trials (log-odds scale)
hist_prior <- map_prior(
  y  = qlogis(c(0.28, 0.35, 0.31)),   # historical response rates, as log-odds
  se = c(0.30, 0.26, 0.34),           # their standard errors
  outcome_type = "single_arm_log_odds"
)
plot(hist_prior)

# Sensitivity analysis
sa <- sensitivity_grid(
  prior,
  data_summary = list(type = "binary", x = 18, n = 40),
  param_grid   = list(alpha = seq(1, 8, 0.5), beta = seq(2, 20, 1)),
  target       = c("posterior_mean", "prob_efficacy"),
  threshold    = 0.30
)
plot_tornado(sa)

# Robust and sceptical priors
rob  <- robust_prior(prior, vague_weight = 0.20)
scep <- sceptical_prior(null_value = 0.20, family = "beta",
                        strength = "moderate")

# Generate regulatory report
prior_report(
  prior           = prior,
  conflict        = cd,
  sensitivity     = sa,
  robust_prior    = rob,
  sceptical_prior = scep,
  output_format   = "html",
  trial_name      = "TRIAL-001",
  sponsor         = "Example Pharma Ltd",
  author          = "N.P., Biostatistician"
)

# Launch the Shiny app
run_app()

Vignettes

Vignette Covers
bayprior-introduction Full end-to-end workflow overview
prior-elicitation All six families and three elicitation methods
conflict-diagnostics All four data types; univariate and multivariate
sensitivity-analysis Grid sensitivity, tornado plots, CrI tracking
robust-priors MAP priors from historical trials, robust mixture, sceptical, and power priors
regulatory-reporting Report generation and compliance checklist
using-with-rstanarm-brms Passing bayprior priors to rstanarm and brms models
browseVignettes("bayprior")

Documentation

Documentation

Full documentation — vignettes, changelog, and cheat sheet. Function help is available in R via help(package = "bayprior") or ?bayprior.


Code of Conduct

Please note that the bayprior project is released with a Contributor Code of Conduct. By contributing to this project, you agree to abide by its terms.


References