Sensitivity Analysis

knitr::opts_chunk$set(echo = TRUE, fig.width = 7, fig.height = 4,
                      message = FALSE, warning = FALSE)
library(bayprior)

Why Sensitivity Analysis Is Required

The FDA's 2026 draft guidance, Use of Bayesian Methodology in Clinical Trials of Drug and Biological Products, calls for sensitivity analyses that demonstrate trial conclusions are robust to plausible variations in prior specification.

bayprior provides two dedicated functions:

Sensitivity analysis is fully independent of conflict diagnostics. You can run it without first running prior_conflict() by supplying data_summary directly.


Supported Data Types

| type = | Conjugate update | param_grid names | |---|---|---| | "binary" | Beta-Binomial | alpha, beta | | "continuous" | Normal-Normal | mu, sigma | | "poisson" | Gamma-Poisson | shape, rate | | "survival" | Gamma-Exponential | shape, rate |


Binary Data

prior    <- elicit_beta(mean = 0.30, sd = 0.10, method = "moments",
                        label = "Response rate")
data_obs <- list(type = "binary", x = 14, n = 40)
sa <- sensitivity_grid(
  prior        = prior,
  data_summary = data_obs,
  param_grid   = list(alpha = seq(1, 8, 1), beta = seq(2, 20, 2)),
  target       = c("posterior_mean", "prob_efficacy"),
  threshold    = 0.30
)
sa$influence_scores
plot_tornado(sa)
plot_sensitivity(sa, target = "posterior_mean")

Credible Interval Sensitivity

cri_sa <- sensitivity_cri(
  prior        = prior,
  data_summary = data_obs,
  param_grid   = list(alpha = seq(1, 8, 1), beta = seq(2, 20, 2)),
  cri_level    = 0.95
)
cri_sa$influence_scores
plot_sensitivity(cri_sa, target = "cri_width")

Poisson / Count Data {#poisson}

Sensitivity with Poisson data uses Gamma-Poisson conjugate updating.

prior_ae  <- elicit_gamma(mean = 0.15, sd = 0.06, method = "moments",
                           label = "AE rate (per person-year)")
data_pois <- list(type = "poisson", x = 18, n = 120)

sa_pois <- sensitivity_grid(
  prior        = prior_ae,
  data_summary = data_pois,
  param_grid   = list(shape = seq(2, 10, 1), rate = seq(5, 40, 5)),
  target       = c("posterior_mean", "prob_efficacy"),
  threshold    = 0.20
)
sa_pois$influence_scores
plot_tornado(sa_pois)
plot_sensitivity(sa_pois, target = "posterior_mean")

Survival / Time-to-Event Data {#survival}

Sensitivity with survival data uses Gamma-Exponential conjugate updating.

prior_hz  <- elicit_exponential(mean = 0.05, method = "moments",
                                 label = "OS hazard rate")
data_surv <- list(type = "survival", x = 30, n = 600)

sa_surv <- sensitivity_grid(
  prior        = prior_hz,
  data_summary = data_surv,
  param_grid   = list(shape = seq(1, 5, 0.5), rate = seq(5, 30, 5)),
  target       = c("posterior_mean", "prob_efficacy"),
  threshold    = 0.10
)
sa_surv$influence_scores
plot_tornado(sa_surv)
cri_surv <- sensitivity_cri(
  prior        = prior_hz,
  data_summary = data_surv,
  param_grid   = list(shape = seq(1, 5, 0.5), rate = seq(5, 30, 5)),
  cri_level    = 0.95
)
plot_sensitivity(cri_surv, target = "cri_width")

Continuous Endpoints

prior_cont <- elicit_normal(mean = 0.0, sd = 0.3, method = "moments",
                             label = "Log odds ratio")
sa_cont <- sensitivity_grid(
  prior        = prior_cont,
  data_summary = list(type = "continuous", x = 0.20, sd = 0.25, n = 60),
  param_grid   = list(mu = seq(-0.5, 0.5, 0.1), sigma = seq(0.1, 0.8, 0.1)),
  target       = c("posterior_mean", "posterior_sd")
)
plot_tornado(sa_cont)

Interpreting Influence Scores

| Score | Sensitivity | Implication | |---|---|---| | < 0.05 | Not sensitive | Prior has negligible influence | | 0.05 – 0.15 | Moderate | Report alongside primary estimate | | > 0.15 | Sensitive | Consider robust prior; emphasise data |


Mixture Prior Sensitivity

e1  <- elicit_beta(mean = 0.25, sd = 0.08, method = "moments",
                   expert_id = "E1", label = "ORR")
e2  <- elicit_beta(mean = 0.40, sd = 0.10, method = "moments",
                   expert_id = "E2", label = "ORR")
mix <- aggregate_experts(list(E1 = e1, E2 = e2), weights = c(0.5, 0.5))

sa_mix <- sensitivity_grid(
  prior        = mix,
  data_summary = list(type = "binary", x = 14, n = 40),
  param_grid   = list(alpha = seq(1, 8, 1), beta = seq(2, 16, 2)),
  target       = "posterior_mean"
)
plot_tornado(sa_mix)

Note: for mixture priors, the grid varies the dominant component's parameters. A compatibility warning is shown in the Shiny app.


Regulatory Reporting

Include in the clinical study report:

  1. Influence score table — one row per posterior quantity
  2. Tornado plot — ordered from most to least sensitive
  3. Heatmap — for the primary efficacy quantity
  4. Classification statement — sensitive / not sensitive per quantity

All three are generated automatically by prior_report() when a bayprior_sensitivity object is supplied.


References



Try the bayprior package in your browser

Any scripts or data that you put into this service are public.

bayprior documentation built on Aug. 27, 2026, 1:09 a.m.