Skip to content

Commit 5b75e48

Browse files
Merge pull request #476 from Merck/472-ensure-integer-sample-size-and-number-of-events
472 ensure integer sample size and number of events
2 parents 4b0c51b + 774d5a1 commit 5b75e48

File tree

8 files changed

+87
-12
lines changed

8 files changed

+87
-12
lines changed

DESCRIPTION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Package: gsDesign2
22
Title: Group Sequential Design with Non-Constant Effect
3-
Version: 1.1.2.23
3+
Version: 1.1.2.24
44
Authors@R: c(
55
person("Keaven", "Anderson", email = "keaven_anderson@merck.com", role = c("aut")),
66
person("Yilong", "Zhang", email = "elong0527@gmail.com", role = c("aut")),

R/gs_power_ahr.R

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,12 @@
5353
#' @param tol Tolerance parameter for boundary convergence (on Z-scale).
5454
#' @param interval An interval that is presumed to include the time at which
5555
#' expected event count is equal to targeted event.
56+
#' @param integer Logical value integer whether it is an integer design
57+
#' (i.e., integer sample size and events) or not. This argument is commonly
58+
#' used when creating integer design via [to_integer()].
5659
#'
57-
#' @return A tibble with columns `Analysis`, `Bound`, `Z`, `Probability`,
58-
#' `theta`, `Time`, `AHR`, `Events`.
60+
#' @return A tibble with columns `analysis`, `bound`, `z`, `probability`,
61+
#' `theta`, `time`, `ahr`, `event`.
5962
#' Contains a row for each analysis and each bound.
6063
#'
6164
#' @details
@@ -159,7 +162,8 @@ gs_power_ahr <- function(
159162
info_scale = c("h0_h1_info", "h0_info", "h1_info"),
160163
r = 18,
161164
tol = 1e-6,
162-
interval = c(.01, 1000)) {
165+
interval = c(.01, 1000),
166+
integer = FALSE) {
163167
# Get the number of analysis
164168
n_analysis <- max(length(event), length(analysis_time), na.rm = TRUE)
165169

@@ -197,6 +201,19 @@ gs_power_ahr <- function(
197201
interval = interval
198202
)
199203

204+
# if both events and sample size are integers, then elaborate the info and info0
205+
if (integer) {
206+
207+
# elaborate info0
208+
q_e <- ratio / (1 + ratio)
209+
q_c <- 1 - q_e
210+
x$info0 <- event * q_e * q_c
211+
212+
# elaborate info
213+
q <- event / x$event
214+
x$info <- x$info * q
215+
}
216+
200217
# Given the above statistical information, calculate the power ----
201218
y_h1 <- gs_power_npe(
202219
theta = x$theta,

R/gs_power_wlr.R

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,8 @@ gs_power_wlr <- function(enroll_rate = define_enroll_rate(duration = c(2, 2, 10)
181181
approx = "asymptotic",
182182
r = 18,
183183
tol = 1e-6,
184-
interval = c(.01, 1000)) {
184+
interval = c(.01, 1000),
185+
integer = FALSE) {
185186
# check of inputted sample size
186187
input_sample_size <- sum(enroll_rate$rate * enroll_rate$duration)
187188

R/to_integer.R

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,19 @@ to_integer.gs_design <- function(x, sample_size = TRUE, ...) {
308308
analysis_time = NULL
309309
)
310310

311-
upar_new$timing <- info_with_new_event$info / max(info_with_new_event$info)
311+
# ensure info0 is based on integer sample size calculation
312+
# as as they become a slight different number due to the `enroll_rate`
313+
q_e <- x$input$ratio / (1 + x$input$ratio)
314+
q_c <- 1 - q_e
315+
info_with_new_event$info0 <- event_new * q_e * q_c
316+
317+
# ensure info is based on integer sample size calculation
318+
# as as they become a slight different number due to the `enroll_rate`
319+
q <- event_new / event
320+
info_with_new_event$info <- x$analysis$info * q
321+
322+
# update timing
323+
upar_new$timing <- info_with_new_event$info0 / max(info_with_new_event$info0)
312324
}
313325
}
314326

@@ -338,7 +350,8 @@ to_integer.gs_design <- function(x, sample_size = TRUE, ...) {
338350
test_lower = x$input$test_lower,
339351
binding = x$input$binding,
340352
info_scale = x$input$info_scale, r = x$input$r, tol = x$input$tol,
341-
interval = c(0.01, max(x$analysis$time) + 100)
353+
interval = c(0.01, max(x$analysis$time) + 100),
354+
integer = TRUE
342355
)
343356
if (is_wlr) power_args[c("weight", "approx")] <- x$input[c("weight", "approx")]
344357
x_new <- do.call(if (is_wlr) gs_power_wlr else gs_power_ahr, power_args)

man/gs_power_ahr.Rd

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

man/gs_power_wlr.Rd

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

tests/testthat/test-developer-gs_power_ahr.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,4 +137,4 @@ test_that("Use default lower, lpar and test_lower", {
137137
# return an error message asking for total spend for the futility test
138138
expect_error(gs_power_ahr(analysis_time = c(24, 36),
139139
event = c(50, 100)))
140-
})
140+
})

tests/testthat/test-developer-to_integer.R

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,37 @@ test_that("The IA nominal p-value is the same as the IA alpha spending.", {
1717
gsDesign::sfLDOF(alpha = 0.025, t = 18 / 30)$spend[1]
1818
)
1919
})
20+
21+
test_that("The statistcial information under null equals to event/4 udner equal randomization.", {
22+
enroll_rate <- define_enroll_rate(duration = c(2, 2, 2, 6),
23+
rate = 1:4)
24+
fail_rate <- define_fail_rate(duration = Inf,
25+
fail_rate = log(2) / 10,
26+
hr = .7,
27+
dropout_rate = 0.001)
28+
29+
alpha <- 0.025
30+
beta <- 0.1
31+
ratio <- 1
32+
33+
x <- gs_design_ahr(
34+
enroll_rate = enroll_rate, fail_rate = fail_rate,
35+
ratio = ratio,
36+
beta = beta,
37+
alpha = alpha,
38+
# Information fraction at analyses and trial duration
39+
info_frac = c(0.6, 0.8, 1),
40+
analysis_time = 48,
41+
# Function and parameter(s) for upper spending bound
42+
upper = gs_spending_bound,
43+
upar = list(sf = gsDesign::sfLDOF, total_spend = alpha, param = NULL),
44+
test_upper = c(FALSE, TRUE, TRUE),
45+
lower = gs_spending_bound,
46+
lpar = list(sf = gsDesign::sfHSD, total_spend = beta, param = -4) ,
47+
test_lower = c(TRUE, FALSE,FALSE),
48+
binding = FALSE
49+
) |>
50+
to_integer()
51+
52+
expect_true(all(x$analysis$info0 - x$analysis$event / 4 == 0))
53+
})

0 commit comments

Comments
 (0)