Skip to content

Commit 39823ae

Browse files
committed
add visuals to interpret power and type-i error
1 parent 21377f4 commit 39823ae

28 files changed

Lines changed: 605 additions & 333 deletions

NAMESPACE

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ export(bcts)
2020
export(bcts_calibrate_betaBinom_conj)
2121
export(bcts_one_interim)
2222
export(bcts_power_betaBinom_conj)
23-
export(bcts_run_betaBinom_conj)
2423
export(bcts_two_interim)
2524
export(bcts_type1_betaBinom_conj)
2625
export(dose_selection)
@@ -37,14 +36,15 @@ export(plot_prior_weight)
3736
export(posterior)
3837
export(power)
3938
export(power_fisher)
39+
export(rct_beta_calibrate)
4040
export(rct_beta_power)
4141
export(rct_power_beta_binom_cpp_vec)
4242
export(run_bcts_app)
43+
export(sat_betabinom_power)
44+
export(sat_betabinom_power_exact)
45+
export(sat_betabinom_type1)
46+
export(sat_betabinom_type1_exact)
4347
export(sim_rct_normal)
44-
export(singlearm_beta_power)
45-
export(singlearm_beta_power_exact)
46-
export(singlearm_beta_type1)
47-
export(singlearm_beta_type1_exact)
4848
export(ssre)
4949
import(ggplot2)
5050
importFrom(Rcpp,evalCpp)

R/RcppExports.R

Lines changed: 93 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,64 @@
11
# Generated by using Rcpp::compileAttributes() -> do not edit by hand
22
# Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393
33

4+
#' Estimate Power for Bayesian RCT Using Beta-Binomial Conjugate Model (C++)
5+
#'
6+
#' Performs Monte Carlo simulation to estimate the statistical power of a
7+
#' Bayesian randomized controlled trial (RCT) using conjugate Beta-Binomial
8+
#' models for the control and treatment arms. The function supports flat priors
9+
#' and power priors on the control arm, and allows for fully customizable
10+
#' baseline Beta prior parameters for both arms.
11+
#'
12+
#' A trial is declared successful if the posterior probability
13+
#' \eqn{Pr(\theta_t - \theta_c > M \mid \text{data}) \ge \gamma}.
14+
#'
15+
#' @param B Integer. Number of simulated trials.
16+
#' @param p_c,p_t Numeric in \[0, 1\]. True response probabilities in the control and treatment arms.
17+
#' @param n_c,n_t Integers. Sample sizes in the control and treatment arms.
18+
#' @param M Numeric. Margin on the risk-difference scale:
19+
#' - Negative for non-inferiority,
20+
#' - Zero for equivalence,
21+
#' - Positive for superiority.
22+
#' @param threshold Numeric in (0, 1). Posterior probability threshold \eqn{\gamma}.
23+
#' @param prior Character. Type of prior to use:
24+
#' - `"flat"`: independent Beta priors for both arms,
25+
#' - `"power"`: power prior on the control arm, flat prior on treatment.
26+
#' @param prior_args List of prior hyperparameters. The following elements are supported:
27+
#' - `a0`: Discount factor for historical control data (only used if `prior = "power"`).
28+
#' - `y_0`, `n_0`: Number of responses and total patients in the historical control data.
29+
#' - `a_base_c`, `b_base_c`: Shape parameters of the Beta prior for the control arm.
30+
#' - `a_base_t`, `b_base_t`: Shape parameters of the Beta prior for the treatment arm.
31+
#' - Defaults for all `a_base_*` and `b_base_*` values are 1 (i.e., flat prior).
32+
#' @param n_draws Integer. Number of posterior draws per trial for estimating the probability.
33+
#' @param show_progress Logical. Show progress bar in the console.
34+
#'
35+
#' @return A logical vector of length `B`, indicating for each trial whether the decision
36+
#' criterion was met (i.e., trial declared successful).
37+
#'
38+
#' @details This function uses Rcpp and vectorized binomial simulation to increase speed.
39+
#' Posterior samples are drawn from Beta distributions parameterized using either flat priors
40+
#' or power priors (for control) combined with observed trial data.
41+
#'
42+
#' @examples
43+
#' prior_args <- list(
44+
#' a0 = 0.5, y_0 = 20, n_0 = 30,
45+
#' a_base_c = 1, b_base_c = 1,
46+
#' a_base_t = 2, b_base_t = 2
47+
#' )
48+
#' decisions <- rct_power_beta_binom_cpp_vec(
49+
#' B = 1000, p_c = 0.8, p_t = 0.8,
50+
#' n_c = 25, n_t = 25,
51+
#' M = -0.1, threshold = 0.9,
52+
#' prior = "power", prior_args = prior_args,
53+
#' n_draws = 2000, show_progress = FALSE
54+
#' )
55+
#' mean(decisions) # Estimated power
56+
#'
57+
#' @export
58+
rct_power_beta_binom_cpp_vec <- function(B, p_c, p_t, n_c, n_t, M, threshold, prior, prior_args, n_draws, show_progress = TRUE) {
59+
.Call(`_bcts_rct_power_beta_binom_cpp_vec`, B, p_c, p_t, n_c, n_t, M, threshold, prior, prior_args, n_draws, show_progress)
60+
}
61+
462
beta_prob_gt <- function(a, b, M, n_draws) {
563
.Call(`_bcts_beta_prob_gt`, a, b, M, n_draws)
664
}
@@ -21,8 +79,11 @@ beta_prob_gt <- function(a, b, M, n_draws) {
2179
#' e.g., \code{M = 0.6}.
2280
#' @param threshold Numeric in \[0, 1\]. Posterior probability cutoff for declaring success,
2381
#' e.g., \code{0.95}.
24-
#' @param prior Character string. Either \code{"flat"} for a Beta(1,1) prior or
25-
#' \code{"beta"} to specify a custom prior using \code{a_base} and \code{b_base}.
82+
#' @param prior Character string specifying the prior distribution.
83+
#' Options are:
84+
#' "flat" for a non-informative Beta(1,1) prior;
85+
#' "jeffreys" for the Jeffreys prior (Beta(0.5, 0.5));
86+
#' "beta" for a custom Beta(\code{a_base}, \code{b_base}) prior (user must provide \code{a_base} and \code{b_base}).
2687
#' @param a_base Numeric. Alpha parameter for the Beta prior (used only if \code{prior = "beta"}).
2788
#' @param b_base Numeric. Beta parameter for the Beta prior (used only if \code{prior = "beta"}).
2889
#'
@@ -35,17 +96,17 @@ beta_prob_gt <- function(a, b, M, n_draws) {
3596
#' }
3697
#'
3798
#' @examples
38-
#' singlearm_beta_power_exact(
99+
#' sat_betabinom_power_exact(
39100
#' p_t = 0.75, n_t = 35, M = 0.6,
40101
#' threshold = 0.95, prior = "flat"
41102
#' )
42103
#'
43-
#' @seealso \code{\link{singlearm_beta_power}} for the simulation-based version.
104+
#' @seealso \code{\link{sat_betabinom_power}} for the simulation-based version.
44105
#'
45106
#' @author Thomas Debray \email{tdebray@fromdatatowisdom.com}
46107
#' @export
47-
singlearm_beta_power_exact <- function(p_t, n_t, M, threshold, prior = "flat", a_base = 1, b_base = 1) {
48-
.Call(`_bcts_singlearm_beta_power_exact`, p_t, n_t, M, threshold, prior, a_base, b_base)
108+
sat_betabinom_power_exact <- function(p_t, n_t, M, threshold, prior = "flat", a_base = 1, b_base = 1) {
109+
.Call(`_bcts_sat_betabinom_power_exact`, p_t, n_t, M, threshold, prior, a_base, b_base)
49110
}
50111

51112
#' @title Compute p-values for a t-distribution with Fixed Degrees of Freedom
@@ -61,24 +122,27 @@ singlearm_beta_power_exact <- function(p_t, n_t, M, threshold, prior = "flat", a
61122
#' @param M Numeric in \[0, 1\]. Decision threshold on the response rate, e.g., \code{M = 0.6}.
62123
#' @param threshold Numeric in \[0, 1\]. Posterior probability cutoff for declaring success,
63124
#' e.g., \code{0.95}.
64-
#' @param prior Character string. Either \code{"flat"} for a Beta(1,1) prior or
65-
#' \code{"beta"} to specify a custom prior using \code{a_base} and \code{b_base}.
125+
#' @param prior Character string specifying the prior distribution.
126+
#' Options are:
127+
#' "flat" for a non-informative Beta(1,1) prior;
128+
#' "jeffreys" for the Jeffreys prior (Beta(0.5, 0.5));
129+
#' "beta" for a custom Beta(\code{a_base}, \code{b_base}) prior (user must provide \code{a_base} and \code{b_base}).
66130
#' @param a_base Numeric. Alpha parameter for the Beta prior (only used if \code{prior = "beta"}).
67131
#' @param b_base Numeric. Beta parameter for the Beta prior (only used if \code{prior = "beta"}).
68132
#' @param show_progress Logical. If \code{TRUE}, prints a simple progress bar to console.
69133
#'
70134
#' @return A list with: estimate (power), mc_se, successes, B.
71135
#'
72136
#' @examples
73-
#' singlearm_beta_power(
137+
#' sat_betabinom_power(
74138
#' B = 1000, p_t = 0.75, n_t = 35, M = 0.60,
75139
#' threshold = 0.95, prior = "flat"
76140
#' )
77141
#'
78142
#' @author Thomas Debray \email{tdebray@fromdatatowisdom.com}
79143
#' @export
80-
singlearm_beta_power <- function(B, p_t, n_t, M, threshold, prior = "flat", a_base = 1, b_base = 1, show_progress = TRUE) {
81-
.Call(`_bcts_singlearm_beta_power`, B, p_t, n_t, M, threshold, prior, a_base, b_base, show_progress)
144+
sat_betabinom_power <- function(B, p_t, n_t, M, threshold, prior = "flat", a_base = 1, b_base = 1, show_progress = TRUE) {
145+
.Call(`_bcts_sat_betabinom_power`, B, p_t, n_t, M, threshold, prior, a_base, b_base, show_progress)
82146
}
83147

84148
#' @title Estimate Type-I Error for Single-Arm Trial
@@ -91,23 +155,27 @@ singlearm_beta_power <- function(B, p_t, n_t, M, threshold, prior = "flat", a_ba
91155
#' @param n_t Integer. Sample size of the treatment arm.
92156
#' @param M Numeric. Decision threshold for θ (on probability scale, e.g., 0.6).
93157
#' @param threshold Posterior probability threshold γ (e.g., 0.95).
94-
#' @param prior "flat" or "beta".
158+
#' @param prior Character string specifying the prior distribution.
159+
#' Options are:
160+
#' "flat" for a non-informative Beta(1,1) prior;
161+
#' "jeffreys" for the Jeffreys prior (Beta(0.5, 0.5));
162+
#' "beta" for a custom Beta(\code{a_base}, \code{b_base}) prior (user must provide \code{a_base} and \code{b_base}).
95163
#' @param a_base Alpha parameter for Beta prior (if prior = "beta").
96164
#' @param b_base Beta parameter for Beta prior (if prior = "beta").
97165
#' @param show_progress Logical. Show progress in console?
98166
#'
99167
#' @return A list with \code{estimate} (type-I error), \code{mc_se}, \code{B}, and \code{rejections}.
100168
#'
101169
#' @examples
102-
#' singlearm_beta_type1(
170+
#' sat_betabinom_type1(
103171
#' B = 1000, n_t = 35, M = 0.6,
104172
#' threshold = 0.95, prior = "flat"
105173
#' )
106174
#'
107175
#' @author Thomas Debray \email{tdebray@fromdatatowisdom.com}
108176
#' @export
109-
singlearm_beta_type1 <- function(B, n_t, M, threshold, prior = "flat", a_base = 1, b_base = 1, show_progress = TRUE) {
110-
.Call(`_bcts_singlearm_beta_type1`, B, n_t, M, threshold, prior, a_base, b_base, show_progress)
177+
sat_betabinom_type1 <- function(B, n_t, M, threshold, prior = "flat", a_base = 1, b_base = 1, show_progress = TRUE) {
178+
.Call(`_bcts_sat_betabinom_type1`, B, n_t, M, threshold, prior, a_base, b_base, show_progress)
111179
}
112180

113181
#' @title Exact Type-I Error for Single-Arm Trial (Beta-Binomial)
@@ -124,8 +192,11 @@ singlearm_beta_type1 <- function(B, n_t, M, threshold, prior = "flat", a_base =
124192
#' @param M Numeric in \[0, 1\]. Decision threshold on the response rate, e.g., \code{M = 0.6}.
125193
#' @param threshold Numeric in \[0, 1\]. Posterior probability cutoff for declaring success,
126194
#' e.g., \code{threshold = 0.95}.
127-
#' @param prior Character string. Either \code{"flat"} for a Beta(1,1) prior or
128-
#' \code{"beta"} to specify a custom prior using \code{a_base} and \code{b_base}.
195+
#' @param prior Character string specifying the prior distribution.
196+
#' Options are:
197+
#' "flat" for a non-informative Beta(1,1) prior;
198+
#' "jeffreys" for the Jeffreys prior (Beta(0.5, 0.5));
199+
#' "beta" for a custom Beta(\code{a_base}, \code{b_base}) prior (user must provide \code{a_base} and \code{b_base})..
129200
#' @param a_base Numeric. Alpha parameter for the Beta prior (only used if \code{prior = "beta"}).
130201
#' @param b_base Numeric. Beta parameter for the Beta prior (only used if \code{prior = "beta"}).
131202
#' @param p_null Optional. True response probability under the null hypothesis (e.g., \code{p_null = 0.6}).
@@ -141,75 +212,17 @@ singlearm_beta_type1 <- function(B, n_t, M, threshold, prior = "flat", a_base =
141212
#'
142213
#' @examples
143214
#' # Type-I error under flat prior at boundary
144-
#' singlearm_beta_type1_exact(n_t = 40, M = 0.65, threshold = 0.9, prior = "flat")
215+
#' sat_betabinom_type1_exact(n_t = 40, M = 0.65, threshold = 0.9, prior = "flat")
145216
#'
146217
#' # Type-I error under true p < M (frequentist view)
147-
#' singlearm_beta_type1_exact(n_t = 40, M = 0.65, threshold = 0.9,
218+
#' sat_betabinom_type1_exact(n_t = 40, M = 0.65, threshold = 0.9,
148219
#' prior = "flat", p_null = 0.60)
149220
#'
150-
#' @seealso \code{\link{singlearm_beta_type1}} for the simulation-based version.
221+
#' @seealso \code{\link{sat_betabinom_type1}} for the simulation-based version.
151222
#'
152223
#' @author Thomas Debray \email{tdebray@fromdatatowisdom.com}
153224
#' @export
154-
singlearm_beta_type1_exact <- function(n_t, M, threshold, prior = "flat", a_base = 1, b_base = 1, p_null = -1.0) {
155-
.Call(`_bcts_singlearm_beta_type1_exact`, n_t, M, threshold, prior, a_base, b_base, p_null)
156-
}
157-
158-
#' Estimate Power for Bayesian RCT Using Beta-Binomial Conjugate Model (C++)
159-
#'
160-
#' Performs Monte Carlo simulation to estimate the statistical power of a
161-
#' Bayesian randomized controlled trial (RCT) using conjugate Beta-Binomial
162-
#' models for the control and treatment arms. The function supports flat priors
163-
#' and power priors on the control arm, and allows for fully customizable
164-
#' baseline Beta prior parameters for both arms.
165-
#'
166-
#' A trial is declared successful if the posterior probability
167-
#' \eqn{Pr(\theta_t - \theta_c > M \mid \text{data}) \ge \gamma}.
168-
#'
169-
#' @param B Integer. Number of simulated trials.
170-
#' @param p_c,p_t Numeric in \[0, 1\]. True response probabilities in the control and treatment arms.
171-
#' @param n_c,n_t Integers. Sample sizes in the control and treatment arms.
172-
#' @param M Numeric. Margin on the risk-difference scale:
173-
#' - Negative for non-inferiority,
174-
#' - Zero for equivalence,
175-
#' - Positive for superiority.
176-
#' @param threshold Numeric in (0, 1). Posterior probability threshold \eqn{\gamma}.
177-
#' @param prior Character. Type of prior to use:
178-
#' - `"flat"`: independent Beta priors for both arms,
179-
#' - `"power"`: power prior on the control arm, flat prior on treatment.
180-
#' @param prior_args List of prior hyperparameters. The following elements are supported:
181-
#' - `a0`: Discount factor for historical control data (only used if `prior = "power"`).
182-
#' - `y_0`, `n_0`: Number of responses and total patients in the historical control data.
183-
#' - `a_base_c`, `b_base_c`: Shape parameters of the Beta prior for the control arm.
184-
#' - `a_base_t`, `b_base_t`: Shape parameters of the Beta prior for the treatment arm.
185-
#' - Defaults for all `a_base_*` and `b_base_*` values are 1 (i.e., flat prior).
186-
#' @param n_draws Integer. Number of posterior draws per trial for estimating the probability.
187-
#' @param show_progress Logical. Show progress bar in the console.
188-
#'
189-
#' @return A logical vector of length `B`, indicating for each trial whether the decision
190-
#' criterion was met (i.e., trial declared successful).
191-
#'
192-
#' @details This function uses Rcpp and vectorized binomial simulation to increase speed.
193-
#' Posterior samples are drawn from Beta distributions parameterized using either flat priors
194-
#' or power priors (for control) combined with observed trial data.
195-
#'
196-
#' @examples
197-
#' prior_args <- list(
198-
#' a0 = 0.5, y_0 = 20, n_0 = 30,
199-
#' a_base_c = 1, b_base_c = 1,
200-
#' a_base_t = 2, b_base_t = 2
201-
#' )
202-
#' decisions <- rct_power_beta_binom_cpp_vec(
203-
#' B = 1000, p_c = 0.8, p_t = 0.8,
204-
#' n_c = 25, n_t = 25,
205-
#' M = -0.1, threshold = 0.9,
206-
#' prior = "power", prior_args = prior_args,
207-
#' n_draws = 2000, show_progress = FALSE
208-
#' )
209-
#' mean(decisions) # Estimated power
210-
#'
211-
#' @export
212-
rct_power_beta_binom_cpp_vec <- function(B, p_c, p_t, n_c, n_t, M, threshold, prior, prior_args, n_draws, show_progress = TRUE) {
213-
.Call(`_bcts_rct_power_beta_binom_cpp_vec`, B, p_c, p_t, n_c, n_t, M, threshold, prior, prior_args, n_draws, show_progress)
225+
sat_betabinom_type1_exact <- function(n_t, M, threshold, prior = "flat", a_base = 1, b_base = 1, p_null = -1.0) {
226+
.Call(`_bcts_sat_betabinom_type1_exact`, n_t, M, threshold, prior, a_base, b_base, p_null)
214227
}
215228

R/rct_beta_calibrate.R

Lines changed: 77 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@
88
#' @param p_c,p_t True response probabilities in control and treatment (numeric in `[0,1]`).
99
#' @param n_c,n_t Sample sizes in control and treatment arms (integers).
1010
#' @param NI_margin Non-inferiority margin on the risk-difference scale (numeric).
11-
#' @param prior Character. Either `"flat"` (Beta(1,1) both arms) or `"power"`
12-
#' (power prior on control); see `prior_args`.
13-
#' @param prior_args List of hyperparameters when `prior = "power"`:
14-
#' - `a0`: discount factor in `[0,1]`
15-
#' - `y_0`, `n_0`: historical control responders and sample size
16-
#' - `a_base`, `b_base`: baseline Beta parameters (defaults `1`, `1`)
11+
#' @param prior Character. Specifies the prior distribution. Options:
12+
#' - `"flat"`: Flat Beta(1,1) prior for both arms (non-informative),
13+
#' - `"jeffreys"`: Jeffreys prior, i.e., Beta(0.5, 0.5) for both arms,
14+
#' - `"power"`: Power prior on the control arm, requires specification in `prior_args`.
15+
#' @param prior_args List of additional prior hyperparameters. Required when `prior = "power"`:
16+
#' - `a0`: Discount factor for historical control data (numeric in `[0,1]`),
17+
#' - `y_0`, `n_0`: Historical control responders and total sample size (integers),
18+
#' - `a_base`, `b_base`: Baseline Beta parameters (default is `1`, `1`).
19+
#' Ignored when `prior` is `"flat"` or `"jeffreys"`.
1720
#' @param B_cal Number of simulated trials for calibration of `gamma`.
1821
#' @param B_power Number of simulated trials for power estimation.
1922
#' @param n_draws Posterior Monte Carlo draws per trial for evaluating `Pr(NI)`.
@@ -39,7 +42,7 @@
3942
#' alpha = 0.10, p_c = .85, p_t = .85,
4043
#' n_c = 29, n_t = 29, NI_margin = -0.20,
4144
#' prior = "flat", B_cal = 1000, B_power = 500,
42-
#' method = "cpp
45+
#' method = "cpp"
4346
#' )
4447
#' res$calibration$gamma
4548
#' res$power
@@ -50,7 +53,7 @@ rct_beta_calibrate <- function(alpha = 0.10,
5053
p_c = 0.85, p_t = 0.85,
5154
n_c = 29, n_t = 29,
5255
NI_margin = -0.20,
53-
prior = c("flat","power"),
56+
prior = c("flat", "jeffreys", "power"),
5457
prior_args = list(),
5558
B_cal = 2000, B_power = 1000,
5659
n_draws = 2000,
@@ -94,3 +97,69 @@ rct_beta_calibrate <- function(alpha = 0.10,
9497
class(out) <- "bayesNI"
9598
out
9699
}
100+
101+
102+
#' Plot calibration trace for calibrated Bayesian NI threshold
103+
#'
104+
#' Visualizes the estimated Type-I error at different posterior thresholds
105+
#' during the calibration procedure.
106+
#'
107+
#' @param x Output from [bcts_calibrate_betaBinom_conj()]
108+
#' @param ... Not used.
109+
#'
110+
#' @return A [ggplot2::ggplot()] object.
111+
#' @export
112+
#'
113+
#' @examples
114+
#' # res <- bcts_calibrate_betaBinom_conj(...) # Run separately
115+
#' # plot(res)
116+
plot.bcts_calibration <- function(x, ...) {
117+
stopifnot(!is.null(x$trace), !is.null(x$type1))
118+
119+
tr <- x$trace
120+
alpha <- x$alpha
121+
gamma <- x$gamma
122+
type1 <- x$type1
123+
124+
# Small vertical offset above CI
125+
offset <- 0.01
126+
127+
ggplot2::ggplot(tr, ggplot2::aes(x = .data$gamma_try, y = .data$type1)) +
128+
ggplot2::geom_errorbar(
129+
ggplot2::aes(ymin = .data$ci_lower, ymax = .data$ci_upper),
130+
width = 0.002, alpha = 0.4
131+
) +
132+
ggplot2::geom_point(size = 2) +
133+
ggplot2::geom_line() +
134+
ggplot2::geom_point(
135+
x = gamma,
136+
y = type1["estimate"],
137+
color = "blue",
138+
size = 3
139+
) +
140+
ggplot2::annotate(
141+
"text",
142+
x = gamma,
143+
y = type1["ci_upper"] + offset,
144+
label = sprintf("gamma = %.3f\nType-I = %.1f%%",
145+
gamma, 100 * type1["estimate"]),
146+
hjust = 0.5,
147+
size = 3.5
148+
) +
149+
ggplot2::geom_hline(
150+
yintercept = alpha,
151+
linetype = "dotted",
152+
color = "red"
153+
) +
154+
ggplot2::labs(
155+
x = expression(gamma),
156+
y = "Estimated Type-I error",
157+
subtitle = sprintf(
158+
"Calibrated gamma = %.3f after %d steps; dotted = target alpha = %.2f",
159+
gamma, x$iters, alpha
160+
)
161+
) +
162+
ggplot2::theme_minimal(base_size = 12)+
163+
ggplot2::scale_y_continuous(labels = scales::label_percent(accuracy = 1))+
164+
ggplot2::scale_x_continuous(labels = scales::label_percent(accuracy = 1))
165+
}

0 commit comments

Comments
 (0)