Skip to content

Commit ce2e114

Browse files
committed
add exact derivations for power and type-i error in single-arm studies
1 parent f8d96f5 commit ce2e114

16 files changed

+710
-82
lines changed

NAMESPACE

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ export(power_fisher)
4040
export(run_bcts_app)
4141
export(sim_rct_normal)
4242
export(singlearm_beta_power)
43+
export(singlearm_beta_power_exact)
4344
export(singlearm_beta_type1)
45+
export(singlearm_beta_type1_exact)
4446
export(ssre)
4547
import(ggplot2)
4648
importFrom(Rcpp,evalCpp)

R/RcppExports.R

Lines changed: 94 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,49 @@ beta_prob_gt <- function(a, b, M, n_draws) {
55
.Call(`_bcts_beta_prob_gt`, a, b, M, n_draws)
66
}
77

8+
#' @title Analytic Power Calculation for Single-Arm Beta-Binomial Design
9+
#'
10+
#' @description Computes the exact Bayesian power for a single-arm binomial trial with a
11+
#' conjugate Beta prior. Power is defined as the probability that the posterior probability
12+
#' that the true response rate exceeds a threshold \code{M} is greater than or equal to
13+
#' a prespecified cutoff \code{threshold}, under a fixed true response rate \code{p_t}.
14+
#'
15+
#' This function avoids simulation and uses a deterministic summation over all possible
16+
#' outcomes of the binomial distribution.
17+
#'
18+
#' @param p_t Numeric in \[0, 1\]. True response probability for the treatment arm.
19+
#' @param n_t Integer. Sample size of the treatment arm.
20+
#' @param M Numeric in \[0, 1\]. Threshold on the response rate for decision-making,
21+
#' e.g., \code{M = 0.6}.
22+
#' @param threshold Numeric in \[0, 1\]. Posterior probability cutoff for declaring success,
23+
#' 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}.
26+
#' @param a_base Numeric. Alpha parameter for the Beta prior (used only if \code{prior = "beta"}).
27+
#' @param b_base Numeric. Beta parameter for the Beta prior (used only if \code{prior = "beta"}).
28+
#'
29+
#' @return A named list with:
30+
#' \describe{
31+
#' \item{\code{estimate}}{The exact Bayesian power (a number between 0 and 1).}
32+
#' \item{\code{mc_se}}{\code{NA_real_}, returned for compatibility (Monte Carlo SE not applicable).}
33+
#' \item{\code{B}}{\code{NA_integer_}, returned for compatibility with simulation version.}
34+
#' \item{\code{successes}}{\code{NA_integer_}, returned for compatibility.}
35+
#' }
36+
#'
37+
#' @examples
38+
#' singlearm_beta_power_exact(
39+
#' p_t = 0.75, n_t = 35, M = 0.6,
40+
#' threshold = 0.95, prior = "flat"
41+
#' )
42+
#'
43+
#' @seealso \code{\link{singlearm_beta_power}} for the simulation-based version.
44+
#'
45+
#' @author Thomas Debray \email{[email protected]}
46+
#' @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)
49+
}
50+
851
#' @title Compute p-values for a t-distribution with Fixed Degrees of Freedom
952
#'
1053
#' @description Simulates a single-arm binomial trial with a conjugate Beta prior,
@@ -22,21 +65,20 @@ beta_prob_gt <- function(a, b, M, n_draws) {
2265
#' \code{"beta"} to specify a custom prior using \code{a_base} and \code{b_base}.
2366
#' @param a_base Numeric. Alpha parameter for the Beta prior (only used if \code{prior = "beta"}).
2467
#' @param b_base Numeric. Beta parameter for the Beta prior (only used if \code{prior = "beta"}).
25-
#' @param n_draws Integer. Number of posterior draws per trial.
2668
#' @param show_progress Logical. If \code{TRUE}, prints a simple progress bar to console.
2769
#'
2870
#' @return A list with: estimate (power), mc_se, successes, B.
2971
#'
3072
#' @examples
3173
#' singlearm_beta_power(
3274
#' B = 1000, p_t = 0.75, n_t = 35, M = 0.60,
33-
#' threshold = 0.95, prior = "flat", n_draws = 2000
75+
#' threshold = 0.95, prior = "flat"
3476
#' )
3577
#'
3678
#' @author Thomas Debray \email{[email protected]}
3779
#' @export
38-
singlearm_beta_power <- function(B, p_t, n_t, M, threshold, prior = "flat", a_base = 1, b_base = 1, n_draws = 2000L, show_progress = TRUE) {
39-
.Call(`_bcts_singlearm_beta_power`, B, p_t, n_t, M, threshold, prior, a_base, b_base, n_draws, show_progress)
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)
4082
}
4183

4284
#' @title Estimate Type-I Error for Single-Arm Trial
@@ -52,20 +94,64 @@ singlearm_beta_power <- function(B, p_t, n_t, M, threshold, prior = "flat", a_ba
5294
#' @param prior "flat" or "beta".
5395
#' @param a_base Alpha parameter for Beta prior (if prior = "beta").
5496
#' @param b_base Beta parameter for Beta prior (if prior = "beta").
55-
#' @param n_draws Number of posterior draws per trial.
5697
#' @param show_progress Logical. Show progress in console?
5798
#'
5899
#' @return A list with \code{estimate} (type-I error), \code{mc_se}, \code{B}, and \code{rejections}.
59100
#'
60101
#' @examples
61102
#' singlearm_beta_type1(
62103
#' B = 1000, n_t = 35, M = 0.6,
63-
#' threshold = 0.95, prior = "flat", n_draws = 2000
104+
#' threshold = 0.95, prior = "flat"
64105
#' )
65106
#'
66107
#' @author Thomas Debray \email{[email protected]}
67108
#' @export
68-
singlearm_beta_type1 <- function(B, n_t, M, threshold, prior = "flat", a_base = 1, b_base = 1, n_draws = 2000L, show_progress = TRUE) {
69-
.Call(`_bcts_singlearm_beta_type1`, B, n_t, M, threshold, prior, a_base, b_base, n_draws, show_progress)
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)
111+
}
112+
113+
#' @title Exact Type-I Error for Single-Arm Trial (Beta-Binomial)
114+
#'
115+
#' @description Computes the exact Type-I error for a single-arm binomial trial
116+
#' using a conjugate Beta prior. This is done by summing the probability of all
117+
#' outcomes where the posterior probability that \eqn{\theta > M} exceeds a
118+
#' decision threshold \code{threshold}, under the null hypothesis.
119+
#'
120+
#' By default, the null hypothesis is \eqn{\theta = M}, but a more conservative
121+
#' frequentist setting can be evaluated by setting \code{p_null < M}.
122+
#'
123+
#' @param n_t Integer. Sample size of the treatment arm.
124+
#' @param M Numeric in \[0, 1\]. Decision threshold on the response rate, e.g., \code{M = 0.6}.
125+
#' @param threshold Numeric in \[0, 1\]. Posterior probability cutoff for declaring success,
126+
#' 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}.
129+
#' @param a_base Numeric. Alpha parameter for the Beta prior (only used if \code{prior = "beta"}).
130+
#' @param b_base Numeric. Beta parameter for the Beta prior (only used if \code{prior = "beta"}).
131+
#' @param p_null Optional. True response probability under the null hypothesis (e.g., \code{p_null = 0.6}).
132+
#' If not specified, defaults to \code{p_null = M} (boundary case).
133+
#'
134+
#' @return A named list with:
135+
#' \describe{
136+
#' \item{\code{estimate}}{Exact Type-I error (a number between 0 and 1).}
137+
#' \item{\code{mc_se}}{\code{NA_real_}, included for compatibility.}
138+
#' \item{\code{B}}{\code{NA_integer_}, included for compatibility.}
139+
#' \item{\code{rejections}}{\code{NA_integer_}, included for compatibility.}
140+
#' }
141+
#'
142+
#' @examples
143+
#' # Type-I error under flat prior at boundary
144+
#' singlearm_beta_type1_exact(n_t = 40, M = 0.65, threshold = 0.9, prior = "flat")
145+
#'
146+
#' # Type-I error under true p < M (frequentist view)
147+
#' singlearm_beta_type1_exact(n_t = 40, M = 0.65, threshold = 0.9,
148+
#' prior = "flat", p_null = 0.60)
149+
#'
150+
#' @seealso \code{\link{singlearm_beta_type1}} for the simulation-based version.
151+
#'
152+
#' @author Thomas Debray \email{[email protected]}
153+
#' @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)
70156
}
71157

R/bcts_power_sat_betaBinom_conj.R

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,46 @@
1-
#' Simulate Bayesian power for a single-arm binomial trial (via Rcpp)
1+
#' Estimate Bayesian power for a single-arm binomial trial
22
#'
3-
#' @param B Number of trial simulations.
3+
#' @description Computes the Bayesian power for a single-arm binomial trial
4+
#' using either simulation or exact analytic summation over all possible outcomes.
5+
#'
6+
#' @param B Number of trial simulations (used only if \code{method = "simulate"}).
47
#' @param p_t True response probability.
58
#' @param n_t Sample size.
69
#' @param M Threshold for success (e.g. 0.6).
710
#' @param threshold Posterior probability threshold (e.g. 0.95).
811
#' @param prior Prior type: "flat" or "beta".
9-
#' @param a_base Alpha of Beta prior (only used if prior = "beta").
10-
#' @param b_base Beta of Beta prior (only used if prior = "beta").
11-
#' @param n_draws Number of posterior samples per trial.
12-
#' @param show_progress Show progress bar?
12+
#' @param a_base Alpha of Beta prior (only used if \code{prior = "beta"}).
13+
#' @param b_base Beta of Beta prior (only used if \code{prior = "beta"}).
14+
#' @param show_progress Show progress bar? (only relevant for simulation).
15+
#' @param method Either \code{"simulate"} (default) or \code{"exact"}.
16+
#'
17+
#' @return A list with \code{estimate} (power), \code{mc_se}, \code{successes}, and \code{B}.
18+
#'
19+
#' @examples
20+
#' singlearm_beta_power(
21+
#' B = 1000, p_t = 0.75, n_t = 35, M = 0.60,
22+
#' threshold = 0.95, prior = "flat", method = "simulate"
23+
#' )
1324
#'
14-
#' @return A list with `estimate` (power), `mc_se`, `successes`, and `B`.
25+
#' singlearm_beta_power(
26+
#' p_t = 0.75, n_t = 35, M = 0.60,
27+
#' threshold = 0.95, prior = "flat", method = "exact"
28+
#' )
1529
#'
1630
#' @export
17-
singlearm_beta_power <- function(B, p_t, n_t, M, threshold,
31+
singlearm_beta_power <- function(B = 10000, p_t, n_t, M, threshold,
1832
prior = "flat", a_base = 1, b_base = 1,
19-
n_draws = 2000, show_progress = TRUE) {
20-
.Call(`_bcts_singlearm_beta_power`, B, p_t, n_t, M, threshold,
21-
prior, a_base, b_base, n_draws, show_progress)
33+
show_progress = TRUE,
34+
method = c("exact", "simulate")) {
35+
method <- match.arg(method)
36+
37+
if (method == "simulate") {
38+
.Call(`_bcts_singlearm_beta_power`,
39+
B, p_t, n_t, M, threshold,
40+
prior, a_base, b_base, show_progress)
41+
} else {
42+
.Call(`_bcts_singlearm_beta_power_exact`,
43+
p_t, n_t, M, threshold,
44+
prior, a_base, b_base)
45+
}
2246
}

R/bcts_type1_sat_betaBinom_conj.R

Lines changed: 58 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,64 @@
1-
#' Simulate Bayesian Type-I error for a single-arm binomial trial (via Rcpp)
1+
#' Estimate Bayesian Type-I Error for a Single-Arm Binomial Trial
22
#'
3-
#' @param B Number of trial simulations.
4-
#' @param n_t Sample size.
5-
#' @param M Decision threshold (also used as null hypothesis value).
6-
#' @param threshold Posterior probability threshold (e.g. 0.95).
7-
#' @param prior Prior type: "flat" or "beta".
8-
#' @param a_base Alpha of Beta prior (only used if prior = "beta").
9-
#' @param b_base Beta of Beta prior (only used if prior = "beta").
10-
#' @param n_draws Number of posterior samples per trial.
11-
#' @param show_progress Show progress bar?
3+
#' @description Computes the Bayesian Type-I error for a single-arm binomial trial
4+
#' using a conjugate Beta prior. You can choose between a simulation-based approach
5+
#' or an exact summation over all possible outcomes under the null hypothesis.
126
#'
13-
#' @return A list with `estimate` (type-I error), `mc_se`, `rejections`, and `B`.
7+
#' By default, the null hypothesis assumes \code{p_null = M} (i.e., testing at the boundary),
8+
#' but for a more conservative frequentist setting, you may specify \code{p_null < M}.
9+
#'
10+
#' @param B Integer. Number of simulations (only used if \code{method = "simulate"}).
11+
#' @param n_t Integer. Sample size of the treatment arm.
12+
#' @param M Numeric. Decision threshold (also used as null value if \code{p_null} not specified).
13+
#' @param threshold Numeric. Posterior probability threshold for declaring success, e.g., \code{0.95}.
14+
#' @param prior Character string. Either \code{"flat"} (Beta(1,1)) or \code{"beta"}.
15+
#' @param a_base Numeric. Alpha parameter of the Beta prior (used only if \code{prior = "beta"}).
16+
#' @param b_base Numeric. Beta parameter of the Beta prior (used only if \code{prior = "beta"}).
17+
#' @param p_null Optional numeric. True response rate under the null hypothesis (default is \code{M}).
18+
#' Only used when \code{method = "exact"}.
19+
#' @param n_draws Ignored. Kept for backward compatibility.
20+
#' @param show_progress Logical. Show progress bar during simulation (only relevant for \code{method = "simulate"}).
21+
#' @param method Character. Either \code{"exact"} (default) or \code{"simulate"}.
22+
#'
23+
#' @return A named list with:
24+
#' \describe{
25+
#' \item{\code{estimate}}{Estimated Type-I error rate.}
26+
#' \item{\code{mc_se}}{Monte Carlo standard error (NA for exact).}
27+
#' \item{\code{rejections}}{Number of rejections (NA for exact).}
28+
#' \item{\code{B}}{Number of simulations (NA for exact).}
29+
#' }
30+
#'
31+
#' @examples
32+
#' # Simulated Type-I error at boundary (p_null = M)
33+
#' singlearm_beta_type1(B = 10000, n_t = 40, M = 0.65, threshold = 0.9, method = "simulate")
34+
#'
35+
#' # Exact Type-I error at boundary (p_null = M)
36+
#' singlearm_beta_type1(n_t = 40, M = 0.65, threshold = 0.9, method = "exact")
37+
#'
38+
#' # Exact Type-I error with p_null < M (frequentist-style)
39+
#' singlearm_beta_type1(n_t = 40, M = 0.65, threshold = 0.9, method = "exact", p_null = 0.60)
40+
#'
41+
#' @seealso \code{\link{singlearm_beta_power}}, \code{\link{singlearm_beta_type1_exact}}
1442
#'
1543
#' @export
16-
singlearm_beta_type1 <- function(B, n_t, M, threshold,
44+
singlearm_beta_type1 <- function(B = 10000, n_t, M, threshold,
1745
prior = "flat", a_base = 1, b_base = 1,
18-
n_draws = 2000, show_progress = TRUE) {
19-
.Call(`_bcts_singlearm_beta_type1`, B, n_t, M, threshold,
20-
prior, a_base, b_base, n_draws, show_progress)
46+
p_null = NULL,
47+
n_draws = 2000, # ignored
48+
show_progress = TRUE,
49+
method = c("exact", "simulate")) {
50+
method <- match.arg(method)
51+
52+
if (method == "simulate") {
53+
.Call(`_bcts_singlearm_beta_type1`,
54+
B, n_t, M, threshold,
55+
prior, a_base, b_base, show_progress)
56+
} else {
57+
# Default p_null = M if not specified
58+
if (is.null(p_null)) p_null <- M
59+
60+
.Call(`_bcts_singlearm_beta_type1_exact`,
61+
n_t, M, threshold,
62+
prior, a_base, b_base, p_null)
63+
}
2164
}

inst/app/app.R

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,8 @@ server <- function(input, output, session) {
410410
prior = prior_type,
411411
a_base = a_base,
412412
b_base = b_base,
413-
n_draws = n_draws,
413+
#n_draws = n_draws,
414+
method = "exact",
414415
show_progress = FALSE
415416
)
416417

man/singlearm_beta_power.Rd

Lines changed: 25 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)