Skip to content

Commit c8f92a2

Browse files
committed
enhance display of calibrated gamma
1 parent dc7c71c commit c8f92a2

7 files changed

Lines changed: 145 additions & 62 deletions

NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Generated by roxygen2: do not edit by hand
22

33
S3method(plot,bcts)
4+
S3method(plot,bcts_calibration)
45
S3method(plotFinalSampleSize,bcts)
56
S3method(plotFinalSampleSize,bcts_results)
67
S3method(plotInterimDecisions,bcts)

R/bayesNI-conj-calibrate.R

Lines changed: 91 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
#' Bayesian trial power estimation with BetaBinomial conjugate model
1+
#' Bayesian trial power estimation with Beta-Binomial conjugate model
22
#'
3-
#' Uses Monte Carlo simulation with conjugate BetaBinomial updates
3+
#' Uses Monte Carlo simulation with conjugate Beta-Binomial updates
44
#' (no MCMC) to estimate the probability of declaring trial success
55
#' under user-specified assumptions. A trial is declared successful
66
#' if \eqn{Pr(\theta_t - \theta_c > M | data) \ge \gamma}.
@@ -80,10 +80,10 @@ bcts_power_betaBinom_conj <- function(B = 1000, p_c, p_t, n_c, n_t, M,
8080
B = B, successes = k)
8181
}
8282

83-
#' Empirical Type-I Error for Bayesian Trial (BetaBinomial, conjugate)
83+
#' Empirical Type-I Error for Bayesian Trial (Beta-Binomial, conjugate)
8484
#'
8585
#' Estimates the Type-I error rate for a Bayesian non-inferiority (or
86-
#' superiority) test using conjugate BetaBinomial updates. The test declares
86+
#' superiority) test using conjugate Beta-Binomial updates. The test declares
8787
#' success if the posterior probability \eqn{Pr(\theta_t - \theta_c > M | data)}
8888
#' exceeds a fixed threshold `threshold`. Simulation is conducted under the
8989
#' least-favourable null (LFN), where \eqn{p_t = p_c + M}.
@@ -197,12 +197,12 @@ bcts_type1_betaBinom_conj <- function(B = 2000, p_c, M, n_c, n_t,
197197
)
198198
}
199199

200-
#' Calibrate the posterior threshold gamma (BetaBinomial, conjugate)
200+
#' Calibrate the posterior threshold gamma (Beta-Binomial, conjugate)
201201
#'
202202
#' Finds the posterior probability cutoff \eqn{\gamma} such that the Bayesian
203203
#' decision rule \code{Pr(theta_t - theta_c > M | data) >= gamma} achieves a
204204
#' target Type-I error \code{alpha} under the least-favourable null, using
205-
#' conjugate BetaBinomial updates (no MCMC). A bisection search is performed
205+
#' conjugate Beta-Binomial updates (no MCMC). A bisection search is performed
206206
#' over \code{[lower, upper]} with caching and common random numbers.
207207
#'
208208
#' @param alpha Target Type-I error in `(0,1)`, e.g., `0.10`.
@@ -216,7 +216,7 @@ bcts_type1_betaBinom_conj <- function(B = 2000, p_c, M, n_c, n_t,
216216
#' @param lower,upper Initial bracketing interval for `gamma`, values in `(0,1)`.
217217
#' @param n_draws Posterior Monte Carlo draws per trial.
218218
#' @param tol Absolute tolerance for `|TypeI - alpha|`. If `NULL`, a Monte
219-
#' Carlodriven default is used.
219+
#' Carlo-driven default is used.
220220
#' @param maxit Maximum number of bisection iterations.
221221
#' @param seed RNG seed for generating common random numbers across thresholds.
222222
#' @param show_progress Show a progress bar inside Type-I evaluations.
@@ -227,9 +227,9 @@ bcts_type1_betaBinom_conj <- function(B = 2000, p_c, M, n_c, n_t,
227227
#' `incProgress(1/maxit, detail = sprintf("Iteration %d of %d", iter, maxit))`.
228228
#' @param calibrate_on Character string indicating which Type-I error quantity
229229
#' to match during calibration. One of:
230-
#' * `"point"` use the Monte Carlo **point estimate** of the Type-I error
231-
#' * `"upper"` use the **upper bound** of the 95% Monte Carlo CI
232-
#' * `"lower"` use the **lower bound** of the 95% Monte Carlo CI
230+
#' * `"point"` - use the Monte Carlo **point estimate** of the Type-I error
231+
#' * `"upper"` - use the **upper bound** of the 95% Monte Carlo CI
232+
#' * `"lower"` - use the **lower bound** of the 95% Monte Carlo CI
233233
#' Default is `"point"`.
234234
#'
235235
#' @return A list with:
@@ -410,11 +410,14 @@ bcts_calibrate_betaBinom_conj <- function(alpha = 0.10, p_c, M, n_c, n_t,
410410

411411
if (stop_now) {
412412
if (!is.null(progress_fun)) progress_fun(maxit, maxit) # <- force 100%
413-
return(list(
413+
return(structure(list(
414414
gamma = mid,
415-
type1 = r_mid$estimate,
416-
mc_se = r_mid$mc_se,
417-
mc_ci = r_mid$mc_ci,
415+
type1 = c(
416+
estimate = r_mid$estimate,
417+
mc_se = r_mid$mc_se,
418+
ci_lower = r_mid$ci_lower,
419+
ci_upper = r_mid$ci_upper
420+
),
418421
bracket = c(lower = lo, upper = hi),
419422
iters = i,
420423
B_cal = B_cal,
@@ -427,7 +430,7 @@ bcts_calibrate_betaBinom_conj <- function(alpha = 0.10, p_c, M, n_c, n_t,
427430
n_draws = n_draws, tol = tol, maxit = maxit,
428431
seed = seed, calibrate_on = calibrate_on
429432
)
430-
))
433+
), class = "bcts_calibration"))
431434
}
432435
if (diff > 0) lo <- mid else hi <- mid
433436
}
@@ -445,11 +448,14 @@ bcts_calibrate_betaBinom_conj <- function(alpha = 0.10, p_c, M, n_c, n_t,
445448
type1 = r_mid$estimate, ci_lower = r_mid$ci_lower, ci_upper = r_mid$ci_upper,
446449
metric = m_mid, lo = lo, hi = hi, diff = m_mid - alpha))
447450

448-
list(
451+
structure(list(
449452
gamma = mid,
450-
type1 = r_mid$estimate,
451-
mc_se = r_mid$mc_se,
452-
mc_ci = r_mid$mc_ci,
453+
type1 = c(
454+
estimate = r_mid$estimate,
455+
mc_se = r_mid$mc_se,
456+
ci_lower = r_mid$ci_lower,
457+
ci_upper = r_mid$ci_upper
458+
),
453459
bracket = c(lower = lo, upper = hi),
454460
iters = maxit,
455461
B_cal = B_cal,
@@ -462,7 +468,7 @@ bcts_calibrate_betaBinom_conj <- function(alpha = 0.10, p_c, M, n_c, n_t,
462468
n_draws = n_draws, tol = tol, maxit = maxit,
463469
seed = seed, calibrate_on = calibrate_on
464470
)
465-
)
471+
), class = "bcts_calibration")
466472
}
467473

468474
#' Validate B_cal parameter
@@ -539,3 +545,68 @@ check_alpha_bracketing <- function(calibrate_on, alpha, lower, upper, r_lo, r_hi
539545

540546
invisible(TRUE)
541547
}
548+
549+
#' Plot calibration trace for calibrated Bayesian NI threshold
550+
#'
551+
#' Visualizes the estimated Type-I error at different posterior thresholds
552+
#' during the calibration procedure.
553+
#'
554+
#' @param x Output from [bcts_calibrate_betaBinom_conj()]
555+
#' @param ... Not used.
556+
#'
557+
#' @return A [ggplot2::ggplot()] object.
558+
#' @export
559+
#'
560+
#' @examples
561+
#' # res <- bcts_calibrate_betaBinom_conj(...) # Run separately
562+
#' # plot(res)
563+
plot.bcts_calibration <- function(x, ...) {
564+
stopifnot(!is.null(x$trace), !is.null(x$type1))
565+
566+
tr <- x$trace
567+
alpha <- x$alpha
568+
gamma <- x$gamma
569+
type1 <- x$type1
570+
571+
# Small vertical offset above CI
572+
offset <- 0.01
573+
574+
ggplot2::ggplot(tr, ggplot2::aes(x = .data$gamma_try, y = .data$type1)) +
575+
ggplot2::geom_errorbar(
576+
ggplot2::aes(ymin = .data$ci_lower, ymax = .data$ci_upper),
577+
width = 0.002, alpha = 0.4
578+
) +
579+
ggplot2::geom_point(size = 2) +
580+
ggplot2::geom_line() +
581+
ggplot2::geom_point(
582+
x = gamma,
583+
y = type1["estimate"],
584+
color = "blue",
585+
size = 3
586+
) +
587+
ggplot2::annotate(
588+
"text",
589+
x = gamma,
590+
y = type1["ci_upper"] + offset,
591+
label = sprintf("gamma = %.3f\nType-I = %.1f%%",
592+
gamma, 100 * type1["estimate"]),
593+
hjust = 0.5,
594+
size = 3.5
595+
) +
596+
ggplot2::geom_hline(
597+
yintercept = alpha,
598+
linetype = "dotted",
599+
color = "red"
600+
) +
601+
ggplot2::labs(
602+
x = expression(gamma),
603+
y = "Estimated Type-I error",
604+
subtitle = sprintf(
605+
"Calibrated gamma = %.3f after %d steps; dotted = target alpha = %.2f",
606+
gamma, x$iters, alpha
607+
)
608+
) +
609+
ggplot2::theme_minimal(base_size = 12)+
610+
ggplot2::scale_y_continuous(labels = scales::label_percent(accuracy = 1))+
611+
ggplot2::scale_x_continuous(labels = scales::label_percent(accuracy = 1))
612+
}

inst/app/modules/mod_oc.R

Lines changed: 19 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -46,25 +46,28 @@ mod_oc_server <- function(id, sim, decision_mode) {
4646
# --- OC table --------------------------------------------------------------
4747
output$table <- renderTable({
4848
s <- sim()
49+
mode <- req(decision_mode())
4950
req(!is.null(s))
5051

52+
# Determine if we should use calibration info
53+
use_cal <- identical(mode, "alpha") && !is.null(s$cal)
54+
55+
# --- Type-I Error ---
56+
type1_est <- if (use_cal) fmt_pct(s$cal$type1["estimate"], 1) else fmt_pct(s$t1$estimate, 1)
57+
type1_ci <- if (use_cal) fmt_ci(s$cal$type1["ci_lower"], s$cal$type1["ci_upper"], 1) else fmt_ci(s$t1$ci_lower, s$t1$ci_upper, 1)
58+
type1_mcse <- if (use_cal) fmt_pct(s$cal$type1["mc_se"], 1) else fmt_pct(s$t1$mc_se, 1)
59+
60+
# --- Power (always from s$pw) ---
61+
power_est <- fmt_pct(s$pw$estimate, 1)
62+
power_ci <- fmt_ci(s$pw$ci_lower, s$pw$ci_upper, 1)
63+
power_mcse <- fmt_pct(s$pw$mc_se, 1)
64+
65+
# --- Build table ---
5166
data.frame(
5267
Metric = c("Gamma used", "Type-I error", "Power"),
53-
Estimate = c(
54-
sprintf("%.3f", s$gamma_used),
55-
fmt_pct(s$t1$estimate, 1),
56-
fmt_pct(s$pw$estimate, 1)
57-
),
58-
`95% CI` = c(
59-
"",
60-
fmt_ci(s$t1$ci_lower, s$t1$ci_upper, 1),
61-
fmt_ci(s$pw$ci_lower, s$pw$ci_upper, 1)
62-
),
63-
`MC SE` = c(
64-
"",
65-
fmt_pct(s$t1$mc_se, 1),
66-
fmt_pct(s$pw$mc_se, 1)
67-
),
68+
Estimate = c(sprintf("%.3f", s$gamma_used), type1_est, power_est),
69+
`95% CI` = c("", type1_ci, power_ci),
70+
`MC SE` = c("", type1_mcse, power_mcse),
6871
check.names = FALSE,
6972
stringsAsFactors = FALSE
7073
)
@@ -84,23 +87,7 @@ mod_oc_server <- function(id, sim, decision_mode) {
8487
validate(need(!is.null(s) && !is.null(s$cal),
8588
"Calibration plot available only when γ was calibrated."))
8689

87-
tr <- s$cal$trace
88-
alpha <- s$cal$alpha
89-
gamma <- s$gamma_used
90-
91-
ggplot2::ggplot(tr, ggplot2::aes(x = gamma_try, y = type1)) +
92-
ggplot2::geom_errorbar(ggplot2::aes(ymin = ci_lower, ymax = ci_upper),
93-
width = 0.002, alpha = 0.4) +
94-
ggplot2::geom_point(size = 2) +
95-
ggplot2::geom_line() +
96-
ggplot2::geom_hline(yintercept = alpha, linetype = "dotted", color = "red") +
97-
ggplot2::geom_vline(xintercept = gamma, linetype = "dashed", color = "blue") +
98-
ggplot2::labs(
99-
x = expression(gamma),
100-
y = "Estimated Type-I error",
101-
subtitle = "Points are bisection iterations with 95% CI; dashed = calibrated γ, dotted = target α"
102-
) +
103-
ggplot2::theme_minimal(base_size = 12)
90+
plot(s$cal) # Calls the S3 method plot.bcts_calibration()
10491
})
10592
})
10693
}

man/bcts_calibrate_betaBinom_conj.Rd

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

man/bcts_power_betaBinom_conj.Rd

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

man/bcts_type1_betaBinom_conj.Rd

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

man/plot.bcts_calibration.Rd

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

0 commit comments

Comments
 (0)