prior_q <- elicit_beta(
quantiles = c("0.05" = 0.10, "0.50" = 0.30, "0.95" = 0.60),
expert_id = "Expert_1",
label = "ORR (treatment arm)"
)
print(prior_q)
plot(prior_q)
Ndoh Penn
May 17, 2026
Prior elicitation translates an expert’s subjective beliefs about an unknown quantity into a formal probability distribution. bayprior implements the SHELF methodology (O’Hagan et al., 2006) across six distribution families and three elicitation methods.
| Family | Support | Typical quantity |
|---|---|---|
| Beta | (0, 1) | Response rates, proportions |
| Normal | (−∞, ∞) | Mean differences, log odds ratios |
| Gamma | (0, ∞) | Event rates, median survival |
| Log-Normal | (0, ∞) | Hazard ratios, PK parameters |
| Exponential | (0, ∞) | Constant hazard rates, Poisson rate priors |
| Weibull | (0, ∞) | Non-constant hazard survival times (OS, PFS) |
Moment matching solves analytically: \[\alpha = \mu \left(\frac{\mu(1-\mu)}{\sigma^2} - 1\right), \quad \beta = (1-\mu)\left(\frac{\mu(1-\mu)}{\sigma^2} - 1\right)\]


The Gamma distribution is the conjugate prior for Poisson and exponential likelihoods — a natural choice for event rate priors.

\[\text{mean} = \frac{\text{shape}}{\text{rate}}, \quad \text{SD} = \frac{\sqrt{\text{shape}}}{\text{rate}}\]

The Exponential distribution models a constant hazard rate \(\lambda > 0\). The mean is \(1/\lambda\) and the SD equals the mean. It is the conjugate prior likelihood for Gamma priors on Poisson rates, and is appropriate when the hazard rate is assumed constant over time.
Use cases: adverse event rates (events per person-time), constant-hazard survival endpoints, Poisson rate priors for count data.
Direct rate specification (= \(\lambda\)):
Fit the rate from expert-specified quantiles:
With Poisson/survival data:
\[\text{Prior: Exponential}(\lambda) \approx \text{Gamma}(1, \lambda)\] \[\text{Posterior: Gamma}(1 + x,\; \lambda + n)\]
where \(x\) = observed events, \(n\) = exposure time or follow-up.
The Weibull distribution is the most widely used model for survival analysis. It is parameterised by shape \(k > 0\) and scale \(\lambda > 0\):
\[\text{mean} = \lambda\,\Gamma(1 + 1/k), \quad \text{SD} = \lambda\sqrt{\Gamma(1 + 2/k) - \Gamma(1 + 1/k)^2}\]
Shape parameter interpretation:
| Shape \(k\) | Hazard | Clinical scenario |
|---|---|---|
| \(k < 1\) | Decreasing | Early frailty, selection |
| \(k = 1\) | Constant | Equivalent to Exponential |
| \(k > 1\) | Increasing | Ageing, post-surgical deterioration |
Specify prior mean and SD — bayprior solves for shape and scale via Nelder-Mead optimisation:
Direct shape and scale specification:
The Weibull distribution does not have a closed-form conjugate prior. When used in prior_conflict() or sensitivity_grid(), bayprior applies a Normal approximation to the posterior matched to the Weibull’s fit_summary moments.
The interactive chip-allocation method for clinical experts unfamiliar with probability distributions. In the Shiny app this is fully interactive; programmatically:

e1 <- elicit_beta(mean = 0.25, sd = 0.08, method = "moments",
expert_id = "Oncologist_1", label = "ORR")
e2 <- elicit_beta(mean = 0.35, sd = 0.10, method = "moments",
expert_id = "Oncologist_2", label = "ORR")
e3 <- elicit_beta(
quantiles = c("0.10" = 0.15, "0.50" = 0.30, "0.90" = 0.52),
expert_id = "Statistician", label = "ORR"
)
consensus <- aggregate_experts(
priors = list(Oncologist_1 = e1, Oncologist_2 = e2, Statistician = e3),
weights = c(0.40, 0.35, 0.25),
method = "linear"
)
plot(consensus)
bayprior validates compatibility before pooling. Priors with incompatible supports (e.g. Beta + Normal) are blocked with an error. Same-support cross-family pooling (e.g. Gamma + Exponential) proceeds with a warning noting that sensitivity analysis will use the dominant component’s parameters.
# Same positive support — allowed (Gamma + Exponential)
g1 <- elicit_gamma(mean = 5, sd = 2, method = "moments",
expert_id = "E1", label = "Rate")
exp1 <- elicit_exponential(mean = 0.2, method = "moments",
expert_id = "E2", label = "Rate")
pool_pos <- aggregate_experts(
priors = list(E1 = g1, E2 = exp1),
weights = c(0.5, 0.5)
)
print(pool_pos)All elicit_*() functions return a "bayprior" S3 object:
List of 7
$ dist : chr "beta"
$ params :List of 2
$ method : chr "quantile"
$ expert_id : chr "Expert_1"
$ label : chr "ORR (treatment arm)"
$ input :List of 1
$ fit_summary:List of 5
- attr(*, "class")= chr "bayprior"
| Slot | Content |
|---|---|
$dist |
Family: "beta", "normal", "gamma", "lognormal", "exponential", "weibull", "mixture" |
$params |
Named list of fitted hyperparameters |
$fit_summary |
mean, sd, q025, q500, q975 |
$method |
Elicitation method |
$expert_id |
Expert identifier |
$label |
Quantity label |