Skip to content

Commit bf4db9c

Browse files
Merge pull request #535 from jdblischak/multiple-alpha
Add arg `alpha` to `gs_bound_summary()` to report multiple efficacy bounds
2 parents 0502c82 + f1d5b5b commit bf4db9c

File tree

3 files changed

+130
-4
lines changed

3 files changed

+130
-4
lines changed

R/gs_bound_summary.R

+34-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#' Summarizes the efficacy and futility bounds for each analysis.
44
#'
55
#' @param x design object
6+
#' @param alpha vector of alpha values to compute additional efficacy columns
67
#'
78
#' @return A data frame
89
#'
@@ -17,8 +18,36 @@
1718
#' x <- gs_design_wlr(info_frac = c(.25, .75, 1), analysis_time = c(12, 25, 36))
1819
#' gs_bound_summary(x)
1920
#'
21+
#' # Report multiple efficacy bounds (only supported for AHR designs)
22+
#' x <- gs_design_ahr(analysis_time = 1:3*12, alpha = 0.0125)
23+
#' gs_bound_summary(x, alpha = c(0.025, 0.05))
24+
#'
2025
#' @export
21-
gs_bound_summary <- function(x) {
26+
gs_bound_summary <- function(x, alpha = NULL) {
27+
if (is.null(alpha)) return(gs_bound_summary_single(x))
28+
if (!inherits(x, "ahr")) stop("The argument `alpha` is only supported for AHR design objects")
29+
if (!is.numeric(alpha)) stop("The argument `alpha` must be a numeric vector")
30+
31+
# Support multiple alphas
32+
alpha_original <- x[["input"]][["alpha"]]
33+
alpha_new <- unique(sort(c(alpha, alpha_original)))
34+
col_efficacy_name <- paste0("\u03b1=", alpha_new)
35+
outlist <- vector("list", length = length(alpha_new))
36+
for (i in seq_along(alpha_new)) {
37+
if (alpha_new[i] == alpha_original) {
38+
outlist[[i]] <- gs_bound_summary_single(x, col_efficacy_name[i])
39+
} else {
40+
x_updated <- gs_update_ahr(x, alpha = alpha_new[i])
41+
x_updated_bounds <- gs_bound_summary_single(x_updated, col_efficacy_name[i])
42+
outlist[[i]] <- x_updated_bounds[col_efficacy_name[i]]
43+
}
44+
}
45+
out <- Reduce(cbind, outlist)
46+
out <- out[, c("Analysis", "Value", col_efficacy_name, "Futility")]
47+
return(out)
48+
}
49+
50+
gs_bound_summary_single <- function(x, col_efficacy_name = "Efficacy") {
2251
# Input
2352
analysis <- x$analysis
2453
bound <- x$bound
@@ -79,13 +108,15 @@ gs_bound_summary <- function(x) {
79108
col_futility <- c(col_futility, as.numeric(row_futility))
80109
}
81110

82-
col_futility <- round(col_futility, 4)
83111
col_efficacy <- round(col_efficacy, 4)
112+
col_futility <- round(col_futility, 4)
84113

85-
data.frame(
114+
out <- data.frame(
86115
Analysis = col_analysis,
87116
Value = col_value,
88117
Efficacy = col_efficacy,
89118
Futility = col_futility
90119
)
120+
colnames(out)[3] <- col_efficacy_name
121+
return(out)
91122
}

man/gs_bound_summary.Rd

+7-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/test-developer-gs_bound_summary.R

+89
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,92 @@ test_that("gs_bound_summary() uses correct HR label", {
2525
expect_identical(x_wlr_bound$Value[5], "P(Cross) if wAHR=0.8")
2626

2727
})
28+
29+
test_that("gs_bound_summary() uses correct column names", {
30+
# column names for single implicit alpha
31+
col_expected <- c("Analysis", "Value", "Efficacy", "Futility")
32+
x <- gs_design_ahr(info_frac = c(.25, .75, 1), analysis_time = c(12, 25, 36))
33+
x_bound <- gs_bound_summary(x)
34+
expect_equal(colnames(x_bound), col_expected)
35+
36+
# column names for multiple alpha values
37+
col_expected <- c("Analysis", "Value", "α=0.0125", "α=0.025", "α=0.05", "Futility")
38+
x <- gs_design_ahr(analysis_time = 1:3*12, alpha = 0.0125)
39+
x_bound <- gs_bound_summary(x, alpha = c(0.025, 0.05))
40+
expect_identical(colnames(x_bound), col_expected)
41+
})
42+
43+
test_that("gs_bound_summary() supports multiple alpha values", {
44+
# gs_design_ahr()
45+
x_0125 <- gs_design_ahr(analysis_time = 1:3 * 12, alpha = 0.0125)
46+
x_0250 <- gs_update_ahr(x_0125, alpha = 0.0250)
47+
x_0500 <- gs_update_ahr(x_0125, alpha = 0.0500)
48+
49+
x_0125_bound <- gs_bound_summary(x_0125)
50+
x_0250_bound <- gs_bound_summary(x_0250)
51+
x_0500_bound <- gs_bound_summary(x_0500)
52+
53+
expected <- cbind(
54+
x_0125_bound[, c("Analysis", "Value")],
55+
`α=0.0125` = x_0125_bound[, "Efficacy"],
56+
`α=0.025` = x_0250_bound[, "Efficacy"],
57+
`α=0.05` = x_0500_bound[, "Efficacy"],
58+
x_0125_bound[, "Futility", drop = FALSE]
59+
)
60+
x_bound <- gs_bound_summary(x_0125, alpha = c(0.025, 0.05))
61+
expect_equal(x_bound, expected)
62+
63+
# gs_power_ahr()
64+
x_0250 <- gs_power_ahr(lpar = list(sf = gsDesign::sfLDOF, total_spend = 0.1))
65+
expect_equal(x_0250[["input"]][["alpha"]], 0.0250)
66+
expect_equal(x_0250[["input"]][["upar"]][["total_spend"]], 0.0250)
67+
x_0125 <- gs_update_ahr(x_0250, alpha = 0.0125)
68+
x_0500 <- gs_update_ahr(x_0250, alpha = 0.0500)
69+
70+
x_0125_bound <- gs_bound_summary(x_0125)
71+
x_0250_bound <- gs_bound_summary(x_0250)
72+
x_0500_bound <- gs_bound_summary(x_0500)
73+
74+
expected <- cbind(
75+
x_0250_bound[, c("Analysis", "Value")],
76+
`α=0.0125` = x_0125_bound[, "Efficacy"],
77+
`α=0.025` = x_0250_bound[, "Efficacy"],
78+
`α=0.05` = x_0500_bound[, "Efficacy"],
79+
x_0250_bound[, "Futility", drop = FALSE]
80+
)
81+
x_bound <- gs_bound_summary(x_0250, alpha = c(0.0125, 0.05))
82+
expect_equal(x_bound, expected)
83+
})
84+
85+
test_that("The arg `alpha` is only supported for AHR design objects", {
86+
x_wlr <- gs_design_wlr(info_frac = c(.25, .75, 1), analysis_time = c(12, 25, 36))
87+
88+
expect_error(
89+
gs_bound_summary(x_wlr, alpha = 0.5),
90+
"The argument `alpha` is only supported for AHR design objects"
91+
)
92+
})
93+
94+
test_that("The arg `alpha` is required to be a numeric vector", {
95+
x <- gs_design_ahr(info_frac = c(.25, .75, 1), analysis_time = c(12, 25, 36))
96+
97+
expect_error(
98+
gs_bound_summary(x, alpha = "alternative"),
99+
"The argument `alpha` must be a numeric vector"
100+
)
101+
})
102+
103+
test_that("Edge case: when arg `alpha` matches original alpha", {
104+
# Redundantly specifying the original alpha does not affect results
105+
x <- gs_design_ahr(analysis_time = 1:3*12, alpha = 0.0125)
106+
107+
x_bound <- gs_bound_summary(x, alpha = c(0.025, 0.05))
108+
x_bound_redundant <- gs_bound_summary(x, alpha = c(0.0125, 0.025, 0.05))
109+
expect_equal(x_bound_redundant, x_bound)
110+
111+
# Only specifying the original alpha only affects the column name
112+
x_bound <- gs_bound_summary(x)
113+
x_bound_same <- gs_bound_summary(x, alpha = 0.0125)
114+
expect_equal(colnames(x_bound_same)[3], "α=0.0125")
115+
expect_equal(unname(x_bound_same), unname(x_bound))
116+
})

0 commit comments

Comments
 (0)