From 5678911fd759141c62dd258eaa07a3cab4efbcd6 Mon Sep 17 00:00:00 2001 From: John Blischak Date: Thu, 22 Feb 2024 13:44:42 -0500 Subject: [PATCH 01/13] Convert `example("sim_gs_n")` to tests from commit 306de0d --- tests/testthat/test-unvalidated-sim_gs_n.R | 216 +++++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 tests/testthat/test-unvalidated-sim_gs_n.R diff --git a/tests/testthat/test-unvalidated-sim_gs_n.R b/tests/testthat/test-unvalidated-sim_gs_n.R new file mode 100644 index 00000000..06f1cf18 --- /dev/null +++ b/tests/testthat/test-unvalidated-sim_gs_n.R @@ -0,0 +1,216 @@ +# 2024-02-22: Converted `example("sim_gs_n")` to tests from commit 306de0d +# https://github.com/Merck/simtrial/tree/306de0dbe380fdb1e906a59f34bf3871d3ee5312 + +library(gsDesign2) +# parameters for enrollment +enroll_rampup_duration <- 4 # duration for enrollment ramp up +enroll_duration <- 16 # total enrollment duration +enroll_rate <- define_enroll_rate(duration = c(enroll_rampup_duration, + enroll_duration - enroll_rampup_duration), + rate = c(10, 30)) +# parameters for treatment effect +delay_effect_duration <- 3 # delay treatment effect in months +median_col <- 9 # survival median of the control arm +median_exp <- c(9, 14) # survival median of the experimental arm +dropout_rate <- 0.001 +fail_rate <- define_fail_rate(duration = c(delay_effect_duration, 100), + fail_rate = log(2) / median_col, + hr = median_col / median_exp, + dropout_rate = dropout_rate) +# other related parameters +alpha <- 0.025 # type I error +beta <- 0.1 # type II error +ratio <- 1 # randomization ratio (exp:col) +# Define cuttings of 2 IAs and 1 FA +# IA1 +# The 1st interim analysis will occur at the later of the following 3 conditions: +# - At least 20 months have passed since the start of the study +# - At least 100 events have occurred +# - At least 20 months have elapsed after enrolling 200/400 subjects, with a +# minimum of 20 months follow-up +# However, if events accumulation is slow, we will wait for a maximum of 24 months. +ia1 <- get_analysis_date(data, + planned_calendar_time = 20, + target_event_overall = 100, + max_extension_for_target_event = 24, + min_n_overall = 200, + min_followup = 20) |> quote() +# IA2 +# The 2nd interim analysis will occur at the later of the following 3 conditions: +# - At least 32 months have passed since the start of the study +# - At least 250 events have occurred +# - At least 10 months after IA1 +# However, if events accumulation is slow, we will wait for a maximum of 34 months. +ia2 <- get_analysis_date(data, + planned_calendar_time = 32, + target_event_overall = 200, + max_extension_for_target_event = 34, + min_time_after_previous_analysis = 10) |> quote() +# FA +# The final analysis will occur at the later of the following 2 conditions: +# - At least 45 months have passed since the start of the study +# - At least 300 events have occurred +fa <- get_analysis_date(data, + planned_calendar_time = 45, + target_event_overall = 350) |> quote() + + +test_that("Test 1: regular logrank test", { + observed <- sim_gs_n( + n_sim = 3, + sample_size = 400, + enroll_rate = enroll_rate, + fail_rate = fail_rate, + test = wlr(data, weight = fh(rho = 0, gamma = 0)) |> quote(), + cutting = list(ia1 = ia1, ia2 = ia2, fa = fa), + seed = 2024 + ) + expected <- data.frame( + rho = numeric(9), + gamma = numeric(9), + z = c( + -3.7486049782713247, -4.53034007934394, -4.316452743033609, + -3.4771440155825752, -3.8631501353780324, -3.2777779731288317, + -3.075862925191481, -3.619345457605645, -4.2225917786532925 + ), + analysis = rep(1:3, 3), + cut_date = c(24, 32, 45, 24, 32, 46.219327415802894, 24, 32, 50.86585486314699), + sim_id = rep(1:3, each = 3L) + ) + expect_equal(observed, expected) +}) + +test_that("Test 2: weighted logrank test by FH(0, 0.5)", { + observed <- sim_gs_n( + n_sim = 3, + sample_size = 400, + enroll_rate = enroll_rate, + fail_rate = fail_rate, + test = wlr(data, weight = fh(rho = 0, gamma = 0.5)) |> quote(), + cutting = list(ia1 = ia1, ia2 = ia2, fa = fa), + seed = 2024 + ) + expected <- data.frame( + rho = numeric(9), + gamma = rep(0.5, 9L), + z = c( + -4.149161171743935, -4.778107819550277, -4.2607297587160256, + -3.605092910242299, -3.945081123231263, -2.919179640988388, + -3.1432278107909206, -3.640458610667732, -4.243289152457 + ), + analysis = rep(1:3, 3), + cut_date = c(24, 32, 45, 24, 32, 46.219327415802894, 24, 32, 50.86585486314699), + sim_id = rep(1:3, each = 3L) + ) + expect_equal(observed, expected) +}) + +test_that("Test 3: weighted logrank test by MB(6)", { + observed <- sim_gs_n( + n_sim = 3, + sample_size = 400, + enroll_rate = enroll_rate, + fail_rate = fail_rate, + test = wlr(data, weight = mb(delay = 3)) |> quote(), + cutting = list(ia1 = ia1, ia2 = ia2, fa = fa), + seed = 2024 + ) + expected <- data.frame( + z = c( + -3.797133894694147, -4.581330588107247, -4.3496437937060906, + -3.5011312494121394, -3.886541892591609, -3.2792862684447983, + -3.114079263266195, -3.6587146250230145, -4.2632793831797855 + ), + analysis = rep(1:3, 3), + cut_date = c(24, 32, 45, 24, 32, 46.219327415802894, 24, 32, 50.86585486314699), + sim_id = rep(1:3, each = 3L) + ) + expect_equal(observed, expected) +}) + +test_that("Test 4: weighted logrank test by early zero (6)", { + observed <- sim_gs_n( + n_sim = 3, + sample_size = 400, + enroll_rate = enroll_rate, + fail_rate = fail_rate, + test = wlr(data, weight = early_zero(6)) |> quote(), + cutting = list(ia1 = ia1, ia2 = ia2, fa = fa), + seed = 2024 + ) + expected <- data.frame( + z = c( + -4.552617167258777, -5.188572984743822, -4.686073828268738, + -3.185533497487861, -3.5975030245947046, -2.786930008687834, + -2.3673440974318556, -3.0630537456426414, -3.7816194091003705 + ), + analysis = rep(1:3, 3), + cut_date = c(24, 32, 45, 24, 32, 46.219327415802894, 24, 32, 50.86585486314699), + sim_id = rep(1:3, each = 3L) + ) + expect_equal(observed, expected) +}) + +test_that("Test 5: RMST", { + observed <- sim_gs_n( + n_sim = 3, + sample_size = 400, + enroll_rate = enroll_rate, + fail_rate = fail_rate, + test = rmst(data, tau = 20) |> quote(), + cutting = list(ia1 = ia1, ia2 = ia2, fa = fa), + seed = 2024 + ) + expected <- data.frame( + rmst_arm1 = c( + 12.466259284156251, 12.444204897288326, 12.425100778728808, + 12.392111715564337, 12.496963791557544, 12.479119007501355, 12.62769367846186, + 12.737915554271744, 12.740241766667666 + ), + rmst_arm0 = c( + 9.585107633112955, 9.591073977478539, 9.590592780789704, 9.824721964671674, + 10.097271436421035, 10.110783864663125, 10.340195893022198, + 10.289798076615766, 10.261299533752227 + ), + rmst_diff = c( + 2.8811516510432966, 2.8531309198097876, 2.834507997939104, 2.567389750892662, + 2.3996923551365086, 2.36833514283823, 2.287497785439662, 2.4481174776559786, + 2.478942232915438 + ), + z = c( + 3.7899815357169184, 3.991862864282945, 3.980100861311682, 3.474868814723485, + 3.2950209410683957, 3.2541151987300845, 2.9805344295194454, + 3.3009521580248022, 3.3504301652133 + ), + analysis = rep(1:3, 3), + cut_date = c(24, 32, 45, 24, 32, 46.219327415802894, 24, 32, 50.86585486314699), + sim_id = rep(1:3, each = 3L) + ) + expect_equal(observed, expected) +}) + + +test_that("Test 6: maxcombo (FH(0,0) + FH(0, 0.5))", { + observed <- sim_gs_n( + n_sim = 3, + sample_size = 400, + enroll_rate = enroll_rate, + fail_rate = fail_rate, + test = maxcombo(data, + test1 = wlr(data, rho = 0, gamma = 0) |> quote(), + test2 = wlr(data, rho = 0, gamma = 0.5) |> quote()) |> quote(), + cutting = list(ia1 = ia1, ia2 = ia2, fa = fa), + seed = 2024 + ) + expected <- data.frame( + p_value = c( + 2.6155386454673746e-05, 1.4330486162172917e-06, 1.247801863046849e-05, + 0.0002358380298724816, 6.130077643518028e-05, 0.0007667834024346343, + 0.001216230102102256, 0.00020471863687732128, 1.7249355113824194e-05 + ), + analysis = rep(1:3, 3), + cut_date = c(24, 32, 45, 24, 32, 46.219327415802894, 24, 32, 50.86585486314699), + sim_id = rep(1:3, each = 3L) + ) + expect_equal(observed, expected) +}) From da12f83d9c28c3476fa99a742d8301a1a2d4c1df Mon Sep 17 00:00:00 2001 From: John Blischak Date: Thu, 22 Feb 2024 13:58:47 -0500 Subject: [PATCH 02/13] Pass test function args via ... for sim_gs_n() --- DESCRIPTION | 2 +- R/sim_gs_n.R | 47 +++++++++++++--------- man/sim_gs_n.Rd | 45 +++++++++++++-------- tests/testthat/test-unvalidated-sim_gs_n.R | 34 +++++++++------- 4 files changed, 77 insertions(+), 51 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 5f26d104..0293b865 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: simtrial Type: Package Title: Clinical Trial Simulation -Version: 0.3.2.4 +Version: 0.3.2.5 Authors@R: c( person("Keaven", "Anderson", email = "keaven_anderson@merck.com", role = c("aut")), person("Yilong", "Zhang", email = "elong0527@gmail.com", role = c("aut")), diff --git a/R/sim_gs_n.R b/R/sim_gs_n.R index b7463903..ab0c07de 100644 --- a/R/sim_gs_n.R +++ b/R/sim_gs_n.R @@ -18,9 +18,13 @@ #' Simulate group sequantial designs with fixed sample size #' @inheritParams sim_fixed_n -#' @param test a functional call of the test such as \code{wlr()} or \code{maxcombo()} +#' @param test a test function such as \code{\link{wlr}}, +#' \code{\link{maxcombo}}, or \code{\link{rmst}}. The simulated data set is +#' passed as the first positional argument to the test function provided. #' @param cutting a functional call of the cutting for IA(s) and FA, see examples #' @param seed random seed +#' @param ... Arguments passed to the test function provided by the argument +#' \code{test} #' #' @return a data frame summaring the simulation ID, analysis date, z statistics or p-values #' @export @@ -91,9 +95,10 @@ #' sample_size = 400, #' enroll_rate = enroll_rate, #' fail_rate = fail_rate, -#' test = wlr(data, weight = fh(rho = 0, gamma = 0)) |> quote(), +#' test = wlr, #' cutting = list(ia1 = ia1, ia2 = ia2, fa = fa), -#' seed = 2024) +#' seed = 2024, +#' weight = fh(rho = 0, gamma = 0)) #' #' # Test 2: weighted logrank test by FH(0, 0.5) #' sim_gs_n( @@ -101,9 +106,10 @@ #' sample_size = 400, #' enroll_rate = enroll_rate, #' fail_rate = fail_rate, -#' test = wlr(data, weight = fh(rho = 0, gamma = 0.5)) |> quote(), +#' test = wlr, #' cutting = list(ia1 = ia1, ia2 = ia2, fa = fa), -#' seed = 2024) +#' seed = 2024, +#' weight = fh(rho = 0, gamma = 0.5)) #' #' #' # Test 3: weighted logrank test by MB(6) @@ -112,9 +118,10 @@ #' sample_size = 400, #' enroll_rate = enroll_rate, #' fail_rate = fail_rate, -#' test = wlr(data, weight = mb(delay = 3)) |> quote(), +#' test = wlr, #' cutting = list(ia1 = ia1, ia2 = ia2, fa = fa), -#' seed = 2024) +#' seed = 2024, +#' weight = mb(delay = 3)) #' #' # Test 4: weighted logrank test by early zero (6) #' sim_gs_n( @@ -122,9 +129,10 @@ #' sample_size = 400, #' enroll_rate = enroll_rate, #' fail_rate = fail_rate, -#' test = wlr(data, weight = early_zero(6)) |> quote(), +#' test = wlr, #' cutting = list(ia1 = ia1, ia2 = ia2, fa = fa), -#' seed = 2024) +#' seed = 2024, +#' weight = early_zero(6)) #' #' # Test 5: RMST #' sim_gs_n( @@ -132,9 +140,10 @@ #' sample_size = 400, #' enroll_rate = enroll_rate, #' fail_rate = fail_rate, -#' test = rmst(data, tau = 20) |> quote(), +#' test = rmst, #' cutting = list(ia1 = ia1, ia2 = ia2, fa = fa), -#' seed = 2024) +#' seed = 2024, +#' tau = 20) #' #' # Test 6: maxcombo (FH(0,0) + FH(0, 0.5)) #' sim_gs_n( @@ -142,11 +151,11 @@ #' sample_size = 400, #' enroll_rate = enroll_rate, #' fail_rate = fail_rate, -#' test = maxcombo(data, -#' test1 = wlr(data, rho = 0, gamma = 0) |> quote(), -#' test2 = wlr(data, rho = 0, gamma = 0.5) |> quote()) |> quote(), +#' test = maxcombo, #' cutting = list(ia1 = ia1, ia2 = ia2, fa = fa), -#' seed = 2024) +#' seed = 2024, +#' test1 = wlr(data, rho = 0, gamma = 0) |> quote(), +#' test2 = wlr(data, rho = 0, gamma = 0.5) |> quote()) sim_gs_n <- function( # number of simulations n_sim = 1000, @@ -168,11 +177,13 @@ sim_gs_n <- function( block = rep(c("experimental", "control"), 2), # default is to to logrank testing # but alternative tests (such as rmst, maxcombo) can be specified - test = wlr(weight = fh(rho = 0, gamma = 0)) |> quote(), + test = wlr, # cutting for IA(s) and FA cutting = NULL, # random seed - seed = 2024 + seed = 2024, + # arguments passed to `test` + ... ){ # input checking # TODO @@ -208,7 +219,7 @@ sim_gs_n <- function( simu_data_cut <- simu_data |> cut_data_by_date(cut_date[i_analysis]) # test - ans_1sim_new <- eval(test, envir = rlang::env(data = simu_data_cut)) + ans_1sim_new <- test(simu_data_cut, ...) ans_1sim_new$analysis <- i_analysis ans_1sim_new$cut_date <- cut_date[i_analysis] ans_1sim_new$sim_id <- sim_id diff --git a/man/sim_gs_n.Rd b/man/sim_gs_n.Rd index 728a7079..0191f0f1 100644 --- a/man/sim_gs_n.Rd +++ b/man/sim_gs_n.Rd @@ -12,9 +12,10 @@ sim_gs_n( fail_rate = data.frame(stratum = "All", duration = c(3, 100), fail_rate = log(2)/c(9, 18), hr = c(0.9, 0.6), dropout_rate = rep(0.001, 2)), block = rep(c("experimental", "control"), 2), - test = quote(wlr(weight = fh(rho = 0, gamma = 0))), + test = wlr, cutting = NULL, - seed = 2024 + seed = 2024, + ... ) } \arguments{ @@ -35,11 +36,16 @@ for experimental vs. control, and dropout rates by stratum and time period.} \item{block}{As in \code{\link[=sim_pw_surv]{sim_pw_surv()}}. Vector of treatments to be included in each block.} -\item{test}{a functional call of the test such as \code{wlr()} or \code{maxcombo()}} +\item{test}{a test function such as \code{\link{wlr}}, +\code{\link{maxcombo}}, or \code{\link{rmst}}. The simulated data set is +passed as the first positional argument to the test function provided.} \item{cutting}{a functional call of the cutting for IA(s) and FA, see examples} \item{seed}{random seed} + +\item{...}{Arguments passed to the test function provided by the argument +\code{test}} } \value{ a data frame summaring the simulation ID, analysis date, z statistics or p-values @@ -113,9 +119,10 @@ sim_gs_n( sample_size = 400, enroll_rate = enroll_rate, fail_rate = fail_rate, - test = wlr(data, weight = fh(rho = 0, gamma = 0)) |> quote(), + test = wlr, cutting = list(ia1 = ia1, ia2 = ia2, fa = fa), - seed = 2024) + seed = 2024, + weight = fh(rho = 0, gamma = 0)) # Test 2: weighted logrank test by FH(0, 0.5) sim_gs_n( @@ -123,9 +130,10 @@ sim_gs_n( sample_size = 400, enroll_rate = enroll_rate, fail_rate = fail_rate, - test = wlr(data, weight = fh(rho = 0, gamma = 0.5)) |> quote(), + test = wlr, cutting = list(ia1 = ia1, ia2 = ia2, fa = fa), - seed = 2024) + seed = 2024, + weight = fh(rho = 0, gamma = 0.5)) # Test 3: weighted logrank test by MB(6) @@ -134,9 +142,10 @@ sim_gs_n( sample_size = 400, enroll_rate = enroll_rate, fail_rate = fail_rate, - test = wlr(data, weight = mb(delay = 3)) |> quote(), + test = wlr, cutting = list(ia1 = ia1, ia2 = ia2, fa = fa), - seed = 2024) + seed = 2024, + weight = mb(delay = 3)) # Test 4: weighted logrank test by early zero (6) sim_gs_n( @@ -144,9 +153,10 @@ sim_gs_n( sample_size = 400, enroll_rate = enroll_rate, fail_rate = fail_rate, - test = wlr(data, weight = early_zero(6)) |> quote(), + test = wlr, cutting = list(ia1 = ia1, ia2 = ia2, fa = fa), - seed = 2024) + seed = 2024, + weight = early_zero(6)) # Test 5: RMST sim_gs_n( @@ -154,9 +164,10 @@ sim_gs_n( sample_size = 400, enroll_rate = enroll_rate, fail_rate = fail_rate, - test = rmst(data, tau = 20) |> quote(), + test = rmst, cutting = list(ia1 = ia1, ia2 = ia2, fa = fa), - seed = 2024) + seed = 2024, + tau = 20) # Test 6: maxcombo (FH(0,0) + FH(0, 0.5)) sim_gs_n( @@ -164,9 +175,9 @@ sim_gs_n( sample_size = 400, enroll_rate = enroll_rate, fail_rate = fail_rate, - test = maxcombo(data, - test1 = wlr(data, rho = 0, gamma = 0) |> quote(), - test2 = wlr(data, rho = 0, gamma = 0.5) |> quote()) |> quote(), + test = maxcombo, cutting = list(ia1 = ia1, ia2 = ia2, fa = fa), - seed = 2024) + seed = 2024, + test1 = wlr(data, rho = 0, gamma = 0) |> quote(), + test2 = wlr(data, rho = 0, gamma = 0.5) |> quote()) } diff --git a/tests/testthat/test-unvalidated-sim_gs_n.R b/tests/testthat/test-unvalidated-sim_gs_n.R index 06f1cf18..83dc4e35 100644 --- a/tests/testthat/test-unvalidated-sim_gs_n.R +++ b/tests/testthat/test-unvalidated-sim_gs_n.R @@ -61,9 +61,10 @@ test_that("Test 1: regular logrank test", { sample_size = 400, enroll_rate = enroll_rate, fail_rate = fail_rate, - test = wlr(data, weight = fh(rho = 0, gamma = 0)) |> quote(), + test = wlr, cutting = list(ia1 = ia1, ia2 = ia2, fa = fa), - seed = 2024 + seed = 2024, + weight = fh(rho = 0, gamma = 0) ) expected <- data.frame( rho = numeric(9), @@ -86,9 +87,10 @@ test_that("Test 2: weighted logrank test by FH(0, 0.5)", { sample_size = 400, enroll_rate = enroll_rate, fail_rate = fail_rate, - test = wlr(data, weight = fh(rho = 0, gamma = 0.5)) |> quote(), + test = wlr, cutting = list(ia1 = ia1, ia2 = ia2, fa = fa), - seed = 2024 + seed = 2024, + weight = fh(rho = 0, gamma = 0.5) ) expected <- data.frame( rho = numeric(9), @@ -111,9 +113,10 @@ test_that("Test 3: weighted logrank test by MB(6)", { sample_size = 400, enroll_rate = enroll_rate, fail_rate = fail_rate, - test = wlr(data, weight = mb(delay = 3)) |> quote(), + test = wlr, cutting = list(ia1 = ia1, ia2 = ia2, fa = fa), - seed = 2024 + seed = 2024, + weight = mb(delay = 3) ) expected <- data.frame( z = c( @@ -134,9 +137,10 @@ test_that("Test 4: weighted logrank test by early zero (6)", { sample_size = 400, enroll_rate = enroll_rate, fail_rate = fail_rate, - test = wlr(data, weight = early_zero(6)) |> quote(), + test = wlr, cutting = list(ia1 = ia1, ia2 = ia2, fa = fa), - seed = 2024 + seed = 2024, + weight = early_zero(6) ) expected <- data.frame( z = c( @@ -157,9 +161,10 @@ test_that("Test 5: RMST", { sample_size = 400, enroll_rate = enroll_rate, fail_rate = fail_rate, - test = rmst(data, tau = 20) |> quote(), + test = rmst, cutting = list(ia1 = ia1, ia2 = ia2, fa = fa), - seed = 2024 + seed = 2024, + tau = 20 ) expected <- data.frame( rmst_arm1 = c( @@ -189,18 +194,17 @@ test_that("Test 5: RMST", { expect_equal(observed, expected) }) - test_that("Test 6: maxcombo (FH(0,0) + FH(0, 0.5))", { observed <- sim_gs_n( n_sim = 3, sample_size = 400, enroll_rate = enroll_rate, fail_rate = fail_rate, - test = maxcombo(data, - test1 = wlr(data, rho = 0, gamma = 0) |> quote(), - test2 = wlr(data, rho = 0, gamma = 0.5) |> quote()) |> quote(), + test = maxcombo, cutting = list(ia1 = ia1, ia2 = ia2, fa = fa), - seed = 2024 + seed = 2024, + test1 = wlr(data, rho = 0, gamma = 0) |> quote(), + test2 = wlr(data, rho = 0, gamma = 0.5) |> quote() ) expected <- data.frame( p_value = c( From 013ed5e2a0ef3881b5723b1a7fa848917a50bad5 Mon Sep 17 00:00:00 2001 From: John Blischak Date: Thu, 22 Feb 2024 14:34:56 -0500 Subject: [PATCH 03/13] Use function factory create_cutting() to pass cutting functions --- DESCRIPTION | 3 +- NAMESPACE | 1 + R/sim_gs_n.R | 77 +++++++++++++++------- man/create_cutting.Rd | 36 ++++++++++ man/sim_gs_n.Rd | 40 ++++++----- tests/testthat/test-unvalidated-sim_gs_n.R | 32 ++++----- 6 files changed, 132 insertions(+), 57 deletions(-) create mode 100644 man/create_cutting.Rd diff --git a/DESCRIPTION b/DESCRIPTION index 0293b865..903d2d6c 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: simtrial Type: Package Title: Clinical Trial Simulation -Version: 0.3.2.5 +Version: 0.3.2.6 Authors@R: c( person("Keaven", "Anderson", email = "keaven_anderson@merck.com", role = c("aut")), person("Yilong", "Zhang", email = "elong0527@gmail.com", role = c("aut")), @@ -44,7 +44,6 @@ Imports: future, methods, mvtnorm, - rlang, stats, survival, utils diff --git a/NAMESPACE b/NAMESPACE index 20a05c2a..c42f556d 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,6 +1,7 @@ # Generated by roxygen2: do not edit by hand export(counting_process) +export(create_cutting) export(cut_data_by_date) export(cut_data_by_event) export(early_zero) diff --git a/R/sim_gs_n.R b/R/sim_gs_n.R index ab0c07de..e948d6b1 100644 --- a/R/sim_gs_n.R +++ b/R/sim_gs_n.R @@ -16,17 +16,18 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -#' Simulate group sequantial designs with fixed sample size +#' Simulate group sequential designs with fixed sample size #' @inheritParams sim_fixed_n #' @param test a test function such as \code{\link{wlr}}, #' \code{\link{maxcombo}}, or \code{\link{rmst}}. The simulated data set is #' passed as the first positional argument to the test function provided. -#' @param cutting a functional call of the cutting for IA(s) and FA, see examples +#' @param cutting a list of cutting functions created by +#' \code{\link{create_cutting}}, see examples #' @param seed random seed #' @param ... Arguments passed to the test function provided by the argument #' \code{test} #' -#' @return a data frame summaring the simulation ID, analysis date, z statistics or p-values +#' @return a data frame summarizing the simulation ID, analysis date, z statistics or p-values #' @export #' #' @examples @@ -62,12 +63,13 @@ #' # - At least 20 months have elapsed after enrolling 200/400 subjects, with a #' # minimum of 20 months follow-up #' # However, if events accumulation is slow, we will wait for a maximum of 24 months. -#' ia1 <- get_analysis_date(data, -#' planned_calendar_time = 20, -#' target_event_overall = 100, -#' max_extension_for_target_event = 24, -#' min_n_overall = 200, -#' min_followup = 20) |> quote() +#' ia1 <- create_cutting( +#' planned_calendar_time = 20, +#' target_event_overall = 100, +#' max_extension_for_target_event = 24, +#' min_n_overall = 200, +#' min_followup = 20 +#' ) #' #' # IA2 #' # The 2nd interim analysis will occur at the later of the following 3 conditions: @@ -75,19 +77,21 @@ #' # - At least 250 events have occurred #' # - At least 10 months after IA1 #' # However, if events accumulation is slow, we will wait for a maximum of 34 months. -#' ia2 <- get_analysis_date(data, -#' planned_calendar_time = 32, -#' target_event_overall = 200, -#' max_extension_for_target_event = 34, -#' min_time_after_previous_analysis = 10) |> quote() +#' ia2 <- create_cutting( +#' planned_calendar_time = 32, +#' target_event_overall = 200, +#' max_extension_for_target_event = 34, +#' min_time_after_previous_analysis = 10 +#' ) #' #' # FA #' # The final analysis will occur at the later of the following 2 conditions: #' # - At least 45 months have passed since the start of the study #' # - At least 300 events have occurred -#' fa <- get_analysis_date(data, -#' planned_calendar_time = 45, -#' target_event_overall = 350) |> quote() +#' fa <- create_cutting( +#' planned_calendar_time = 45, +#' target_event_overall = 350 +#' ) #' #' # Test 1: regular logrank test #' sim_gs_n( @@ -209,11 +213,7 @@ sim_gs_n <- function( for (i_analysis in 1:n_analysis) { # get cut date - if (i_analysis < n_analysis) { - cut_date[i_analysis] <- cutting[[paste0("ia", i_analysis)]] |> eval(envir = rlang::env(data = simu_data)) - } else { - cut_date[i_analysis] <- cutting[["fa"]] |> eval(envir = rlang::env(data = simu_data)) - } + cut_date[i_analysis] <- cutting[[i_analysis]](data = simu_data) # cut the data simu_data_cut <- simu_data |> cut_data_by_date(cut_date[i_analysis]) @@ -232,3 +232,36 @@ sim_gs_n <- function( } return(ans) } + +#' Create a cutting function +#' +#' Create a cutting function for use with \code{\link{sim_gs_n}} +#' +#' @param ... Arguments passed to \code{\link{get_analysis_date}} +#' +#' @return A function that accepts a data frame of simulated trial data and +#' returns a cut date +#' +#' @export +#' +#' @seealso \code{\link{get_analysis_date}}, \code{\link{sim_gs_n}} +#' +#' @examples +#' # Simulate trial data +#' trial_data <- sim_pw_surv() +#' +#' # Create a cutting function that applies the following 2 conditions: +#' # - At least 45 months have passed since the start of the study +#' # - At least 300 events have occurred +#' cutting <- create_cutting( +#' planned_calendar_time = 45, +#' target_event_overall = 350 +#' ) +#' +#' # Cut the trial data +#' cutting(trial_data) +create_cutting <- function(...) { + function(data) { + get_analysis_date(data, ...) + } +} diff --git a/man/create_cutting.Rd b/man/create_cutting.Rd new file mode 100644 index 00000000..9e7b70eb --- /dev/null +++ b/man/create_cutting.Rd @@ -0,0 +1,36 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/sim_gs_n.R +\name{create_cutting} +\alias{create_cutting} +\title{Create a cutting function} +\usage{ +create_cutting(...) +} +\arguments{ +\item{...}{Arguments passed to \code{\link{get_analysis_date}}} +} +\value{ +A function that accepts a data frame of simulated trial data and +returns a cut date +} +\description{ +Create a cutting function for use with \code{\link{sim_gs_n}} +} +\examples{ +# Simulate trial data +trial_data <- sim_pw_surv() + +# Create a cutting function that applies the following 2 conditions: +# - At least 45 months have passed since the start of the study +# - At least 300 events have occurred +cutting <- create_cutting( + planned_calendar_time = 45, + target_event_overall = 350 +) + +# Cut the trial data +cutting(trial_data) +} +\seealso{ +\code{\link{get_analysis_date}}, \code{\link{sim_gs_n}} +} diff --git a/man/sim_gs_n.Rd b/man/sim_gs_n.Rd index 0191f0f1..95a798e5 100644 --- a/man/sim_gs_n.Rd +++ b/man/sim_gs_n.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/sim_gs_n.R \name{sim_gs_n} \alias{sim_gs_n} -\title{Simulate group sequantial designs with fixed sample size} +\title{Simulate group sequential designs with fixed sample size} \usage{ sim_gs_n( n_sim = 1000, @@ -40,7 +40,8 @@ in each block.} \code{\link{maxcombo}}, or \code{\link{rmst}}. The simulated data set is passed as the first positional argument to the test function provided.} -\item{cutting}{a functional call of the cutting for IA(s) and FA, see examples} +\item{cutting}{a list of cutting functions created by +\code{\link{create_cutting}}, see examples} \item{seed}{random seed} @@ -48,10 +49,10 @@ passed as the first positional argument to the test function provided.} \code{test}} } \value{ -a data frame summaring the simulation ID, analysis date, z statistics or p-values +a data frame summarizing the simulation ID, analysis date, z statistics or p-values } \description{ -Simulate group sequantial designs with fixed sample size +Simulate group sequential designs with fixed sample size } \examples{ library(gsDesign2) @@ -86,12 +87,13 @@ ratio <- 1 # randomization ratio (exp:col) # - At least 20 months have elapsed after enrolling 200/400 subjects, with a # minimum of 20 months follow-up # However, if events accumulation is slow, we will wait for a maximum of 24 months. -ia1 <- get_analysis_date(data, - planned_calendar_time = 20, - target_event_overall = 100, - max_extension_for_target_event = 24, - min_n_overall = 200, - min_followup = 20) |> quote() +ia1 <- create_cutting( + planned_calendar_time = 20, + target_event_overall = 100, + max_extension_for_target_event = 24, + min_n_overall = 200, + min_followup = 20 +) # IA2 # The 2nd interim analysis will occur at the later of the following 3 conditions: @@ -99,19 +101,21 @@ ia1 <- get_analysis_date(data, # - At least 250 events have occurred # - At least 10 months after IA1 # However, if events accumulation is slow, we will wait for a maximum of 34 months. -ia2 <- get_analysis_date(data, - planned_calendar_time = 32, - target_event_overall = 200, - max_extension_for_target_event = 34, - min_time_after_previous_analysis = 10) |> quote() +ia2 <- create_cutting( + planned_calendar_time = 32, + target_event_overall = 200, + max_extension_for_target_event = 34, + min_time_after_previous_analysis = 10 +) # FA # The final analysis will occur at the later of the following 2 conditions: # - At least 45 months have passed since the start of the study # - At least 300 events have occurred -fa <- get_analysis_date(data, - planned_calendar_time = 45, - target_event_overall = 350) |> quote() +fa <- create_cutting( + planned_calendar_time = 45, + target_event_overall = 350 +) # Test 1: regular logrank test sim_gs_n( diff --git a/tests/testthat/test-unvalidated-sim_gs_n.R b/tests/testthat/test-unvalidated-sim_gs_n.R index 83dc4e35..c3fa803e 100644 --- a/tests/testthat/test-unvalidated-sim_gs_n.R +++ b/tests/testthat/test-unvalidated-sim_gs_n.R @@ -29,31 +29,33 @@ ratio <- 1 # randomization ratio (exp:col) # - At least 20 months have elapsed after enrolling 200/400 subjects, with a # minimum of 20 months follow-up # However, if events accumulation is slow, we will wait for a maximum of 24 months. -ia1 <- get_analysis_date(data, - planned_calendar_time = 20, - target_event_overall = 100, - max_extension_for_target_event = 24, - min_n_overall = 200, - min_followup = 20) |> quote() +ia1 <- create_cutting( + planned_calendar_time = 20, + target_event_overall = 100, + max_extension_for_target_event = 24, + min_n_overall = 200, + min_followup = 20 +) # IA2 # The 2nd interim analysis will occur at the later of the following 3 conditions: # - At least 32 months have passed since the start of the study # - At least 250 events have occurred # - At least 10 months after IA1 # However, if events accumulation is slow, we will wait for a maximum of 34 months. -ia2 <- get_analysis_date(data, - planned_calendar_time = 32, - target_event_overall = 200, - max_extension_for_target_event = 34, - min_time_after_previous_analysis = 10) |> quote() +ia2 <- create_cutting( + planned_calendar_time = 32, + target_event_overall = 200, + max_extension_for_target_event = 34, + min_time_after_previous_analysis = 10 +) # FA # The final analysis will occur at the later of the following 2 conditions: # - At least 45 months have passed since the start of the study # - At least 300 events have occurred -fa <- get_analysis_date(data, - planned_calendar_time = 45, - target_event_overall = 350) |> quote() - +fa <- create_cutting( + planned_calendar_time = 45, + target_event_overall = 350 +) test_that("Test 1: regular logrank test", { observed <- sim_gs_n( From e1b35d17915703e63a57d9947f4cd5d62231aaf6 Mon Sep 17 00:00:00 2001 From: John Blischak Date: Thu, 22 Feb 2024 14:50:40 -0500 Subject: [PATCH 04/13] Update copyright year --- LICENSE | 4 ++-- R/counting_process.R | 2 +- R/cut_data_by_date.R | 2 +- R/cut_data_by_event.R | 2 +- R/early_zero_weight.R | 2 +- R/ex1_delayed_effect.R | 2 +- R/ex2_delayed_effect.R | 2 +- R/ex3_cure_with_ph.R | 2 +- R/ex4_belly.R | 2 +- R/ex5_widening.R | 2 +- R/ex6_crossing.R | 2 +- R/fh_weight.R | 2 +- R/fit_pwexp.R | 2 +- R/get_analysis_date.R | 2 +- R/get_cut_date_by_event.R | 2 +- R/global.R | 2 +- R/input_checking.R | 2 +- R/maxcombo.R | 2 +- R/mb_delayed_effect.R | 2 +- R/mb_weight.R | 2 +- R/pvalue_maxcombo.R | 2 +- R/randomize_by_fixed_block.R | 2 +- R/rpwexp.R | 2 +- R/rpwexp_enroll.R | 2 +- R/rpwexp_naive.R | 2 +- R/sim_fixed_n.R | 2 +- R/sim_gs_n.R | 2 +- R/sim_pw_surv.R | 2 +- R/simtrial-package.R | 2 +- R/to_sim_pw_surv.R | 2 +- R/wlr.R | 2 +- R/wlr_weight.R | 2 +- _pkgdown.yml | 2 +- 33 files changed, 34 insertions(+), 34 deletions(-) diff --git a/LICENSE b/LICENSE index 603d1e58..2e3e301d 100644 --- a/LICENSE +++ b/LICENSE @@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. simtrial: Clinical Trial Simulation - Copyright (c) 2023 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. All rights reserved. + Copyright (c) 2024 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. All rights reserved. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: - simtrial Copyright (c) 2023 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. All rights reserved. + simtrial Copyright (c) 2024 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. All rights reserved. This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. diff --git a/R/counting_process.R b/R/counting_process.R index 288a8376..02751327 100644 --- a/R/counting_process.R +++ b/R/counting_process.R @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. +# Copyright (c) 2024 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. # All rights reserved. # # This file is part of the simtrial program. diff --git a/R/cut_data_by_date.R b/R/cut_data_by_date.R index d8224c76..90bc9747 100644 --- a/R/cut_data_by_date.R +++ b/R/cut_data_by_date.R @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. +# Copyright (c) 2024 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. # All rights reserved. # # This file is part of the simtrial program. diff --git a/R/cut_data_by_event.R b/R/cut_data_by_event.R index c17c6d15..3667d8f7 100644 --- a/R/cut_data_by_event.R +++ b/R/cut_data_by_event.R @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. +# Copyright (c) 2024 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. # All rights reserved. # # This file is part of the simtrial program. diff --git a/R/early_zero_weight.R b/R/early_zero_weight.R index 42e769a9..3ee012c6 100644 --- a/R/early_zero_weight.R +++ b/R/early_zero_weight.R @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. +# Copyright (c) 2024 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. # All rights reserved. # # This file is part of the simtrial program. diff --git a/R/ex1_delayed_effect.R b/R/ex1_delayed_effect.R index f8dd7acb..debb3177 100644 --- a/R/ex1_delayed_effect.R +++ b/R/ex1_delayed_effect.R @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. +# Copyright (c) 2024 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. # All rights reserved. # # This file is part of the simtrial program. diff --git a/R/ex2_delayed_effect.R b/R/ex2_delayed_effect.R index 69bd3375..6546d0af 100644 --- a/R/ex2_delayed_effect.R +++ b/R/ex2_delayed_effect.R @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. +# Copyright (c) 2024 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. # All rights reserved. # # This file is part of the simtrial program. diff --git a/R/ex3_cure_with_ph.R b/R/ex3_cure_with_ph.R index 9071b160..20975c4c 100644 --- a/R/ex3_cure_with_ph.R +++ b/R/ex3_cure_with_ph.R @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. +# Copyright (c) 2024 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. # All rights reserved. # # This file is part of the simtrial program. diff --git a/R/ex4_belly.R b/R/ex4_belly.R index 835f10d6..60ce29a2 100644 --- a/R/ex4_belly.R +++ b/R/ex4_belly.R @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. +# Copyright (c) 2024 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. # All rights reserved. # # This file is part of the simtrial program. diff --git a/R/ex5_widening.R b/R/ex5_widening.R index 7e19ec38..c4b489f9 100644 --- a/R/ex5_widening.R +++ b/R/ex5_widening.R @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. +# Copyright (c) 2024 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. # All rights reserved. # # This file is part of the simtrial program. diff --git a/R/ex6_crossing.R b/R/ex6_crossing.R index 5ec12d78..1329db9c 100644 --- a/R/ex6_crossing.R +++ b/R/ex6_crossing.R @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. +# Copyright (c) 2024 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. # All rights reserved. # # This file is part of the simtrial program. diff --git a/R/fh_weight.R b/R/fh_weight.R index 00dc7373..04d998b9 100644 --- a/R/fh_weight.R +++ b/R/fh_weight.R @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. +# Copyright (c) 2024 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. # All rights reserved. # # This file is part of the simtrial program. diff --git a/R/fit_pwexp.R b/R/fit_pwexp.R index 13e0949c..cdd8d140 100644 --- a/R/fit_pwexp.R +++ b/R/fit_pwexp.R @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. +# Copyright (c) 2024 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. # All rights reserved. # # This file is part of the simtrial program. diff --git a/R/get_analysis_date.R b/R/get_analysis_date.R index a703786c..7cbc987a 100644 --- a/R/get_analysis_date.R +++ b/R/get_analysis_date.R @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. +# Copyright (c) 2024 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. # All rights reserved. # # This file is part of the simtrial program. diff --git a/R/get_cut_date_by_event.R b/R/get_cut_date_by_event.R index 1c6c311d..43e7e15d 100644 --- a/R/get_cut_date_by_event.R +++ b/R/get_cut_date_by_event.R @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. +# Copyright (c) 2024 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. # All rights reserved. # # This file is part of the simtrial program. diff --git a/R/global.R b/R/global.R index 79176e64..71c2500d 100644 --- a/R/global.R +++ b/R/global.R @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. +# Copyright (c) 2024 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. # All rights reserved. # # This file is part of the simtrial program. diff --git a/R/input_checking.R b/R/input_checking.R index af17165f..0aa90e69 100644 --- a/R/input_checking.R +++ b/R/input_checking.R @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. +# Copyright (c) 2024 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. # All rights reserved. # # This file is part of the simtrial program. diff --git a/R/maxcombo.R b/R/maxcombo.R index ccad2fff..e6e9d971 100644 --- a/R/maxcombo.R +++ b/R/maxcombo.R @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. +# Copyright (c) 2024 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. # All rights reserved. # # This file is part of the simtrial program. diff --git a/R/mb_delayed_effect.R b/R/mb_delayed_effect.R index 3a3cb042..56580361 100644 --- a/R/mb_delayed_effect.R +++ b/R/mb_delayed_effect.R @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. +# Copyright (c) 2024 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. # All rights reserved. # # This file is part of the simtrial program. diff --git a/R/mb_weight.R b/R/mb_weight.R index ac78791b..7872774d 100644 --- a/R/mb_weight.R +++ b/R/mb_weight.R @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. +# Copyright (c) 2024 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. # All rights reserved. # # This file is part of the simtrial program. diff --git a/R/pvalue_maxcombo.R b/R/pvalue_maxcombo.R index 71308428..273bd862 100644 --- a/R/pvalue_maxcombo.R +++ b/R/pvalue_maxcombo.R @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. +# Copyright (c) 2024 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. # All rights reserved. # # This file is part of the simtrial program. diff --git a/R/randomize_by_fixed_block.R b/R/randomize_by_fixed_block.R index 5d0b0b67..995930ea 100644 --- a/R/randomize_by_fixed_block.R +++ b/R/randomize_by_fixed_block.R @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. +# Copyright (c) 2024 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. # All rights reserved. # # This file is part of the simtrial program. diff --git a/R/rpwexp.R b/R/rpwexp.R index d05cc705..d2011b19 100644 --- a/R/rpwexp.R +++ b/R/rpwexp.R @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. +# Copyright (c) 2024 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. # All rights reserved. # # This file is part of the simtrial program. diff --git a/R/rpwexp_enroll.R b/R/rpwexp_enroll.R index f3d1f6f4..c5e615c7 100644 --- a/R/rpwexp_enroll.R +++ b/R/rpwexp_enroll.R @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. +# Copyright (c) 2024 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. # All rights reserved. # # This file is part of the simtrial program. diff --git a/R/rpwexp_naive.R b/R/rpwexp_naive.R index 64b83d3f..63a93140 100644 --- a/R/rpwexp_naive.R +++ b/R/rpwexp_naive.R @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. +# Copyright (c) 2024 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. # All rights reserved. # # This file is part of the simtrial program. diff --git a/R/sim_fixed_n.R b/R/sim_fixed_n.R index d2e92acd..6348247f 100644 --- a/R/sim_fixed_n.R +++ b/R/sim_fixed_n.R @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. +# Copyright (c) 2024 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. # All rights reserved. # # This file is part of the simtrial program. diff --git a/R/sim_gs_n.R b/R/sim_gs_n.R index e948d6b1..10bc59ca 100644 --- a/R/sim_gs_n.R +++ b/R/sim_gs_n.R @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. +# Copyright (c) 2024 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. # All rights reserved. # # This file is part of the simtrial program. diff --git a/R/sim_pw_surv.R b/R/sim_pw_surv.R index 0c744b2a..f4189957 100644 --- a/R/sim_pw_surv.R +++ b/R/sim_pw_surv.R @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. +# Copyright (c) 2024 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. # All rights reserved. # # This file is part of the simtrial program. diff --git a/R/simtrial-package.R b/R/simtrial-package.R index ec9503a0..6c225d57 100644 --- a/R/simtrial-package.R +++ b/R/simtrial-package.R @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. +# Copyright (c) 2024 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. # All rights reserved. # # This file is part of the pkglite program. diff --git a/R/to_sim_pw_surv.R b/R/to_sim_pw_surv.R index c589c008..537c4f1d 100644 --- a/R/to_sim_pw_surv.R +++ b/R/to_sim_pw_surv.R @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. +# Copyright (c) 2024 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. # All rights reserved. # # This file is part of the simtrial program. diff --git a/R/wlr.R b/R/wlr.R index 3ef300e5..d9f8cd61 100644 --- a/R/wlr.R +++ b/R/wlr.R @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. +# Copyright (c) 2024 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. # All rights reserved. # # This file is part of the simtrial program. diff --git a/R/wlr_weight.R b/R/wlr_weight.R index ff03e7ba..7428b487 100644 --- a/R/wlr_weight.R +++ b/R/wlr_weight.R @@ -1,4 +1,4 @@ -# Copyright (c) 2023 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. +# Copyright (c) 2024 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. # All rights reserved. # # This file is part of the simtrial program. diff --git a/_pkgdown.yml b/_pkgdown.yml index ba44cdad..a3e6a003 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -20,7 +20,7 @@ footer: left: [developed_by, built_with, legal] right: [blank] components: - legal: "
Copyright © 2023 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. All rights reserved." + legal: "
Copyright © 2024 Merck & Co., Inc., Rahway, NJ, USA and its affiliates. All rights reserved." blank: "" reference: From e4dc1af7fee4eb98e7a9947511778b0a2c5b7bc1 Mon Sep 17 00:00:00 2001 From: John Blischak Date: Thu, 22 Feb 2024 14:58:19 -0500 Subject: [PATCH 05/13] Apply Nan's code improvement suggestions https://github.com/Merck/simtrial/pull/195#pullrequestreview-1876654204 --- R/maxcombo.R | 2 +- R/sim_gs_n.R | 4 ++-- R/wlr.R | 6 +++--- R/wlr_weight.R | 12 +++--------- 4 files changed, 9 insertions(+), 15 deletions(-) diff --git a/R/maxcombo.R b/R/maxcombo.R index e6e9d971..44000159 100644 --- a/R/maxcombo.R +++ b/R/maxcombo.R @@ -39,7 +39,7 @@ maxcombo <- function(data, test1, test2, ...){ rho_vector <- NULL gamma_vector <- NULL - for (i in 1:n_test) { + for (i in seq_len(n_test)) { test_i <- get(paste0("test", i)) rho_vector <- c(rho_vector, test_i$rho) gamma_vector <- c(gamma_vector, test_i$gamma) diff --git a/R/sim_gs_n.R b/R/sim_gs_n.R index 10bc59ca..f6ca2b99 100644 --- a/R/sim_gs_n.R +++ b/R/sim_gs_n.R @@ -194,7 +194,7 @@ sim_gs_n <- function( # simulate for n_sim times ans <- NULL - for (sim_id in 1:n_sim) { + for (sim_id in seq_len(n_sim)) { set.seed(seed + sim_id) # generate data simu_data <- sim_pw_surv( @@ -210,7 +210,7 @@ sim_gs_n <- function( cut_date <- rep(-100, n_analysis) ans_1sim <- NULL - for (i_analysis in 1:n_analysis) { + for (i_analysis in seq_len(n_analysis)) { # get cut date cut_date[i_analysis] <- cutting[[i_analysis]](data = simu_data) diff --git a/R/wlr.R b/R/wlr.R index d9f8cd61..84c9d1fa 100644 --- a/R/wlr.R +++ b/R/wlr.R @@ -38,12 +38,12 @@ #' wlr <- function(data, weight){ - if ("fh" %in% class(weight)) { + if (inherits(weight, "fh")) { ans <- data |> counting_process(arm = "experimental") |> fh_weight(rho_gamma = data.frame(rho = weight$rho, gamma = weight$gamma)) - } else if ("mb" %in% class(weight)) { + } else if (inherits(weight, "mb")) { ans <- data |> counting_process(arm = "experimental") |> mb_weight(delay = weight$delay, w_max = weight$w_max) |> @@ -52,7 +52,7 @@ wlr <- function(data, weight){ v = sum(var_o_minus_e * mb_weight^2), z = s / sqrt(v)) |> dplyr::select(z) - } else if ("early_period" %in% class(weight)){ + } else if (inherits(weight, "early_period")) { ans <- data |> counting_process(arm = "experimental") |> early_zero_weight(early_period = weight$early_period) |> diff --git a/R/wlr_weight.R b/R/wlr_weight.R index 7428b487..92ab253a 100644 --- a/R/wlr_weight.R +++ b/R/wlr_weight.R @@ -26,9 +26,7 @@ #' @examples #' fh(rho = 0, gamma = 0.5) fh <- function(rho = 0, gamma = 0){ - ans <- list(rho = rho, gamma = gamma) - class(ans) <- c(class(ans), "fh", "wlr") - return(ans) + structure(list(rho = rho, gamma = gamma), class = c("list", "fh", "wlr")) } #' Magirr and Burman weighting function @@ -45,9 +43,7 @@ fh <- function(rho = 0, gamma = 0){ #' @examples #' mb(delay = 6, w_max = 2) mb <- function(delay = 4, w_max = Inf){ - ans <- list(delay = delay, w_max = w_max) - class(ans) <- c(class(ans), "mb", "wlr") - return(ans) + structure(list(delay = delay, w_max = w_max), class = c("list", "mb", "wlr")) } #' Zero early weighting function @@ -64,7 +60,5 @@ mb <- function(delay = 4, w_max = Inf){ #' @examples #' early_zero(6) early_zero <- function(early_period){ - ans <- list(early_period = early_period) - class(ans) <- c(class(ans), "early_period", "wlr") - return(ans) + structure(list(early_period = early_period), class = c("list", "early_period", "wlr")) } From 16b5b3280ced7cb33c0ab405f0ef7a9c3a3e0e23 Mon Sep 17 00:00:00 2001 From: John Blischak Date: Thu, 22 Feb 2024 15:34:31 -0500 Subject: [PATCH 06/13] Convert wlr() from dplyr to data.table --- DESCRIPTION | 2 +- R/wlr.R | 34 +++++++++++++++++++++++----------- man/wlr.Rd | 3 ++- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 903d2d6c..a9294091 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -35,7 +35,7 @@ BugReports: https://github.com/Merck/simtrial/issues Encoding: UTF-8 LazyData: true VignetteBuilder: knitr -Depends: R (>= 4.1.0) +Depends: R (>= 4.3.0) Imports: Rcpp, data.table (>= 1.12.4), diff --git a/R/wlr.R b/R/wlr.R index 84c9d1fa..f8fd9057 100644 --- a/R/wlr.R +++ b/R/wlr.R @@ -19,9 +19,13 @@ #' Weighted logrank test #' #' @param data cutted dataset generated by sim_pw_surv -#' @param weight weighting functions, such as \code{fh_weight}, \code{mb_weight}, and \code{early_zero_weight}. +#' @param weight weighting functions, such as \code{\link{fh_weight}}, +#' \code{\link{mb_weight}}, and \code{\link{early_zero_weight}}. #' #' @return test results +#' +#' @importFrom data.table setDF setDT +#' #' @export #' @examples #' sim_pw_surv(n = 200) |> @@ -47,20 +51,28 @@ wlr <- function(data, weight){ ans <- data |> counting_process(arm = "experimental") |> mb_weight(delay = weight$delay, w_max = weight$w_max) |> - dplyr::summarize( - s = sum(o_minus_e * mb_weight), - v = sum(var_o_minus_e * mb_weight^2), - z = s / sqrt(v)) |> - dplyr::select(z) + setDT() |> + _[, + .( + s = sum(o_minus_e * mb_weight), + v = sum(var_o_minus_e * mb_weight^2) + ) + ] |> + _[, .(z = s / sqrt(v))] |> + setDF() } else if (inherits(weight, "early_period")) { ans <- data |> counting_process(arm = "experimental") |> early_zero_weight(early_period = weight$early_period) |> - dplyr::summarize( - s = sum(o_minus_e * weight), - v = sum(var_o_minus_e * weight^2), - z = s / sqrt(v)) |> - dplyr::select(z) + setDT() |> + _[, + .( + s = sum(o_minus_e * weight), + v = sum(var_o_minus_e * weight^2) + ) + ] |> + _[, .(z = s / sqrt(v))] |> + setDF() } return(ans) } diff --git a/man/wlr.Rd b/man/wlr.Rd index a718f676..8a017f8a 100644 --- a/man/wlr.Rd +++ b/man/wlr.Rd @@ -9,7 +9,8 @@ wlr(data, weight) \arguments{ \item{data}{cutted dataset generated by sim_pw_surv} -\item{weight}{weighting functions, such as \code{fh_weight}, \code{mb_weight}, and \code{early_zero_weight}.} +\item{weight}{weighting functions, such as \code{\link{fh_weight}}, +\code{\link{mb_weight}}, and \code{\link{early_zero_weight}}.} } \value{ test results From ba1118e1807ee5e99cbfad8d2998cb992bb1f2b9 Mon Sep 17 00:00:00 2001 From: John Blischak Date: Thu, 22 Feb 2024 15:49:57 -0500 Subject: [PATCH 07/13] Don't use native pipe with data.table (requires R >= 4.3.0) --- DESCRIPTION | 2 +- R/wlr.R | 38 ++++++++++++++++++-------------------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index a9294091..903d2d6c 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -35,7 +35,7 @@ BugReports: https://github.com/Merck/simtrial/issues Encoding: UTF-8 LazyData: true VignetteBuilder: knitr -Depends: R (>= 4.3.0) +Depends: R (>= 4.1.0) Imports: Rcpp, data.table (>= 1.12.4), diff --git a/R/wlr.R b/R/wlr.R index f8fd9057..fdd65265 100644 --- a/R/wlr.R +++ b/R/wlr.R @@ -50,29 +50,27 @@ wlr <- function(data, weight){ } else if (inherits(weight, "mb")) { ans <- data |> counting_process(arm = "experimental") |> - mb_weight(delay = weight$delay, w_max = weight$w_max) |> - setDT() |> - _[, - .( - s = sum(o_minus_e * mb_weight), - v = sum(var_o_minus_e * mb_weight^2) - ) - ] |> - _[, .(z = s / sqrt(v))] |> - setDF() + mb_weight(delay = weight$delay, w_max = weight$w_max) + setDT(ans) + ans <- ans[, + .( + s = sum(o_minus_e * mb_weight), + v = sum(var_o_minus_e * mb_weight^2) + ) + ][, .(z = s / sqrt(v))] + setDF(ans) } else if (inherits(weight, "early_period")) { ans <- data |> counting_process(arm = "experimental") |> - early_zero_weight(early_period = weight$early_period) |> - setDT() |> - _[, - .( - s = sum(o_minus_e * weight), - v = sum(var_o_minus_e * weight^2) - ) - ] |> - _[, .(z = s / sqrt(v))] |> - setDF() + early_zero_weight(early_period = weight$early_period) + setDT(ans) + ans <- ans[, + .( + s = sum(o_minus_e * weight), + v = sum(var_o_minus_e * weight^2) + ) + ][, .(z = s / sqrt(v))] + setDF(ans) } return(ans) } From f8a68789d7624db60516c6a082bd8ac291bae606 Mon Sep 17 00:00:00 2001 From: John Blischak Date: Thu, 22 Feb 2024 16:10:57 -0500 Subject: [PATCH 08/13] Add create_cutting() to pkgdown site --- _pkgdown.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/_pkgdown.yml b/_pkgdown.yml index a3e6a003..ac94271d 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -43,6 +43,7 @@ reference: - cut_data_by_event - get_cut_date_by_event - get_analysis_date + - create_cutting - title: "Compute p-values/test statistics" contents: From 2ec28361ef55c8ef6a48234847145b2ca9d65581 Mon Sep 17 00:00:00 2001 From: John Blischak Date: Fri, 23 Feb 2024 10:52:47 -0500 Subject: [PATCH 09/13] Add test for maxcombo() --- tests/testthat/test-unvalidated-maxcombo.R | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 tests/testthat/test-unvalidated-maxcombo.R diff --git a/tests/testthat/test-unvalidated-maxcombo.R b/tests/testthat/test-unvalidated-maxcombo.R new file mode 100644 index 00000000..b984b9f2 --- /dev/null +++ b/tests/testthat/test-unvalidated-maxcombo.R @@ -0,0 +1,12 @@ +# 2024-02-23: Converted `example("maxcombo")` to tests from commit 914c604 +# https://github.com/Merck/simtrial/pull/201/commits/914c6049cf9afb526f2286fdacfabf883050e175 + +test_that("maxcombo returns consistent results", { + set.seed(1) + observed <- sim_pw_surv(n = 200) |> + cut_data_by_event(150) |> + maxcombo(test1 = wlr(data, rho = 0, gamma = 0) |> quote(), + test2 = wlr(data, rho = 0, gamma = 0.5) |> quote()) + expected <- data.frame(p_value = 1.5739680815363144e-06) + expect_equal(observed, expected) +}) From ae86fe7fb0f4613330caf68c2af53c9c63c3f7b4 Mon Sep 17 00:00:00 2001 From: John Blischak Date: Fri, 23 Feb 2024 11:05:56 -0500 Subject: [PATCH 10/13] Change the maxcombo() interface to accept vectors --- DESCRIPTION | 2 +- R/maxcombo.R | 36 ++++++++++------------ R/sim_gs_n.R | 4 +-- man/maxcombo.Rd | 16 +++++----- man/sim_gs_n.Rd | 4 +-- tests/testthat/test-unvalidated-maxcombo.R | 13 ++++++-- tests/testthat/test-unvalidated-sim_gs_n.R | 4 +-- 7 files changed, 43 insertions(+), 36 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 903d2d6c..c90ea708 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: simtrial Type: Package Title: Clinical Trial Simulation -Version: 0.3.2.6 +Version: 0.3.2.7 Authors@R: c( person("Keaven", "Anderson", email = "keaven_anderson@merck.com", role = c("aut")), person("Yilong", "Zhang", email = "elong0527@gmail.com", role = c("aut")), diff --git a/R/maxcombo.R b/R/maxcombo.R index 44000159..c36721bc 100644 --- a/R/maxcombo.R +++ b/R/maxcombo.R @@ -19,37 +19,33 @@ #' Maxcombo test #' #' @param data a tte dataset -#' @param test1 maxcombo test1 -#' @param test2 maxcombo test2 -#' @param ... additional tests +#' @param rho Numeric vector passed to \code{\link{fh_weight}}. Must be greater +#' than or equal to zero. Must be the same length as \code{gamma}. +#' @param gamma Numeric vector passed to \code{\link{fh_weight}}. Must be +#' greater than or equal to zero. Must be the same length as \code{rho}. #' #' @return pvalues #' @export #' +#' @seealso \code{\link{fh_weight}} +#' #' @examples #' sim_pw_surv(n = 200) |> #' cut_data_by_event(150) |> -#' maxcombo(test1 = wlr(data, rho = 0, gamma = 0) |> quote(), -#' test2 = wlr(data, rho = 0, gamma = 0.5) |> quote()) -maxcombo <- function(data, test1, test2, ...){ - all_args <- match.call(expand.dots = FALSE) - args <- all_args[-1] # Exclude the first element (function name) - - n_test <- length(args) - 1 - rho_vector <- NULL - gamma_vector <- NULL - - for (i in seq_len(n_test)) { - test_i <- get(paste0("test", i)) - rho_vector <- c(rho_vector, test_i$rho) - gamma_vector <- c(gamma_vector, test_i$gamma) - } +#' maxcombo(rho = c(0, 0), gamma = c(0, 0.5)) +maxcombo <- function(data, rho, gamma){ + stopifnot( + is.numeric(rho), is.numeric(gamma), + rho >= 0, gamma >= 0, + length(rho) == length(gamma) + ) ans <- data |> counting_process(arm = "experimental") |> fh_weight( - rho_gamma = data.frame(rho = rho_vector, gamma = gamma_vector), - return_corr = TRUE) + rho_gamma = data.frame(rho = rho, gamma = gamma), + return_corr = TRUE + ) ans <- data.frame(p_value = pvalue_maxcombo(ans)) diff --git a/R/sim_gs_n.R b/R/sim_gs_n.R index f6ca2b99..6e3c09f2 100644 --- a/R/sim_gs_n.R +++ b/R/sim_gs_n.R @@ -158,8 +158,8 @@ #' test = maxcombo, #' cutting = list(ia1 = ia1, ia2 = ia2, fa = fa), #' seed = 2024, -#' test1 = wlr(data, rho = 0, gamma = 0) |> quote(), -#' test2 = wlr(data, rho = 0, gamma = 0.5) |> quote()) +#' rho = c(0, 0), +#' gamma = c(0, 0.5)) sim_gs_n <- function( # number of simulations n_sim = 1000, diff --git a/man/maxcombo.Rd b/man/maxcombo.Rd index 35e32c75..28eca247 100644 --- a/man/maxcombo.Rd +++ b/man/maxcombo.Rd @@ -4,16 +4,16 @@ \alias{maxcombo} \title{Maxcombo test} \usage{ -maxcombo(data, test1, test2, ...) +maxcombo(data, rho, gamma) } \arguments{ \item{data}{a tte dataset} -\item{test1}{maxcombo test1} +\item{rho}{Numeric vector passed to \code{\link{fh_weight}}. Must be greater +than or equal to zero. Must be the same length as \code{gamma}.} -\item{test2}{maxcombo test2} - -\item{...}{additional tests} +\item{gamma}{Numeric vector passed to \code{\link{fh_weight}}. Must be +greater than or equal to zero. Must be the same length as \code{rho}.} } \value{ pvalues @@ -24,6 +24,8 @@ Maxcombo test \examples{ sim_pw_surv(n = 200) |> cut_data_by_event(150) |> - maxcombo(test1 = wlr(data, rho = 0, gamma = 0) |> quote(), - test2 = wlr(data, rho = 0, gamma = 0.5) |> quote()) + maxcombo(rho = c(0, 0), gamma = c(0, 0.5)) +} +\seealso{ +\code{\link{fh_weight}} } diff --git a/man/sim_gs_n.Rd b/man/sim_gs_n.Rd index 95a798e5..9aa3741f 100644 --- a/man/sim_gs_n.Rd +++ b/man/sim_gs_n.Rd @@ -182,6 +182,6 @@ sim_gs_n( test = maxcombo, cutting = list(ia1 = ia1, ia2 = ia2, fa = fa), seed = 2024, - test1 = wlr(data, rho = 0, gamma = 0) |> quote(), - test2 = wlr(data, rho = 0, gamma = 0.5) |> quote()) + rho = c(0, 0), + gamma = c(0, 0.5)) } diff --git a/tests/testthat/test-unvalidated-maxcombo.R b/tests/testthat/test-unvalidated-maxcombo.R index b984b9f2..a69f64b8 100644 --- a/tests/testthat/test-unvalidated-maxcombo.R +++ b/tests/testthat/test-unvalidated-maxcombo.R @@ -5,8 +5,17 @@ test_that("maxcombo returns consistent results", { set.seed(1) observed <- sim_pw_surv(n = 200) |> cut_data_by_event(150) |> - maxcombo(test1 = wlr(data, rho = 0, gamma = 0) |> quote(), - test2 = wlr(data, rho = 0, gamma = 0.5) |> quote()) + maxcombo(rho = c(0, 0), gamma = c(0, 0.5)) expected <- data.frame(p_value = 1.5739680815363144e-06) expect_equal(observed, expected) }) + +test_that("maxcombo fails early with bad input", { + input <- observed <- sim_pw_surv(n = 200) |> + cut_data_by_event(150) + expect_error(maxcombo(input, rho = c(-1, 0), gamma = c(0, 0.5))) + expect_error(maxcombo(input, rho = c(0, 0), gamma = c(-1, 0.5))) + expect_error(maxcombo(input, rho = letters[1:2], gamma = c(0, 0.5))) + expect_error(maxcombo(input, rho = c(0, 0), gamma = letters[1:2])) + expect_error(maxcombo(input, rho = c(0), gamma = c(0, 0.5))) +}) diff --git a/tests/testthat/test-unvalidated-sim_gs_n.R b/tests/testthat/test-unvalidated-sim_gs_n.R index c3fa803e..eef6a7e0 100644 --- a/tests/testthat/test-unvalidated-sim_gs_n.R +++ b/tests/testthat/test-unvalidated-sim_gs_n.R @@ -205,8 +205,8 @@ test_that("Test 6: maxcombo (FH(0,0) + FH(0, 0.5))", { test = maxcombo, cutting = list(ia1 = ia1, ia2 = ia2, fa = fa), seed = 2024, - test1 = wlr(data, rho = 0, gamma = 0) |> quote(), - test2 = wlr(data, rho = 0, gamma = 0.5) |> quote() + rho = c(0, 0), + gamma = c(0, 0.5) ) expected <- data.frame( p_value = c( From d450b8a0005c5f253a7afb5532b1070aa9e4a725 Mon Sep 17 00:00:00 2001 From: John Blischak Date: Fri, 23 Feb 2024 11:21:14 -0500 Subject: [PATCH 11/13] Qualify gsDesign2 namespace explicitly in sim_gs_n() tests --- tests/testthat/test-unvalidated-sim_gs_n.R | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/tests/testthat/test-unvalidated-sim_gs_n.R b/tests/testthat/test-unvalidated-sim_gs_n.R index eef6a7e0..5e7acadb 100644 --- a/tests/testthat/test-unvalidated-sim_gs_n.R +++ b/tests/testthat/test-unvalidated-sim_gs_n.R @@ -1,22 +1,25 @@ # 2024-02-22: Converted `example("sim_gs_n")` to tests from commit 306de0d # https://github.com/Merck/simtrial/tree/306de0dbe380fdb1e906a59f34bf3871d3ee5312 -library(gsDesign2) # parameters for enrollment enroll_rampup_duration <- 4 # duration for enrollment ramp up enroll_duration <- 16 # total enrollment duration -enroll_rate <- define_enroll_rate(duration = c(enroll_rampup_duration, - enroll_duration - enroll_rampup_duration), - rate = c(10, 30)) +enroll_rate <- gsDesign2::define_enroll_rate( + duration = c(enroll_rampup_duration, + enroll_duration - enroll_rampup_duration), + rate = c(10, 30) +) # parameters for treatment effect delay_effect_duration <- 3 # delay treatment effect in months median_col <- 9 # survival median of the control arm median_exp <- c(9, 14) # survival median of the experimental arm dropout_rate <- 0.001 -fail_rate <- define_fail_rate(duration = c(delay_effect_duration, 100), - fail_rate = log(2) / median_col, - hr = median_col / median_exp, - dropout_rate = dropout_rate) +fail_rate <- gsDesign2::define_fail_rate( + duration = c(delay_effect_duration, 100), + fail_rate = log(2) / median_col, + hr = median_col / median_exp, + dropout_rate = dropout_rate +) # other related parameters alpha <- 0.025 # type I error beta <- 0.1 # type II error From c8629b3b696e3cde02faf22e354acecabbfef690 Mon Sep 17 00:00:00 2001 From: John Blischak Date: Fri, 23 Feb 2024 11:30:25 -0500 Subject: [PATCH 12/13] Move sim_gs_n() test file scope code to helper functions --- tests/testthat/helper-sim_gs_n.R | 72 ++++++++++++++++ tests/testthat/test-unvalidated-sim_gs_n.R | 95 +++++----------------- 2 files changed, 91 insertions(+), 76 deletions(-) create mode 100644 tests/testthat/helper-sim_gs_n.R diff --git a/tests/testthat/helper-sim_gs_n.R b/tests/testthat/helper-sim_gs_n.R new file mode 100644 index 00000000..fe6104da --- /dev/null +++ b/tests/testthat/helper-sim_gs_n.R @@ -0,0 +1,72 @@ +# helper functions used by test-unvalidated-sim_gs_n.R + +test_enroll_rate <- function() { + # parameters for enrollment + enroll_rampup_duration <- 4 # duration for enrollment ramp up + enroll_duration <- 16 # total enrollment duration + enroll_rate <- gsDesign2::define_enroll_rate( + duration = c(enroll_rampup_duration, + enroll_duration - enroll_rampup_duration), + rate = c(10, 30) + ) + return(enroll_rate) +} + +test_fail_rate <- function() { + # parameters for treatment effect + delay_effect_duration <- 3 # delay treatment effect in months + median_col <- 9 # survival median of the control arm + median_exp <- c(9, 14) # survival median of the experimental arm + dropout_rate <- 0.001 + fail_rate <- gsDesign2::define_fail_rate( + duration = c(delay_effect_duration, 100), + fail_rate = log(2) / median_col, + hr = median_col / median_exp, + dropout_rate = dropout_rate + ) + return(fail_rate) +} + +test_cutting <- function() { + # other related parameters + alpha <- 0.025 # type I error + beta <- 0.1 # type II error + ratio <- 1 # randomization ratio (exp:col) + # Define cuttings of 2 IAs and 1 FA + # IA1 + # The 1st interim analysis will occur at the later of the following 3 conditions: + # - At least 20 months have passed since the start of the study + # - At least 100 events have occurred + # - At least 20 months have elapsed after enrolling 200/400 subjects, with a + # minimum of 20 months follow-up + # However, if events accumulation is slow, we will wait for a maximum of 24 months. + ia1 <- create_cutting( + planned_calendar_time = 20, + target_event_overall = 100, + max_extension_for_target_event = 24, + min_n_overall = 200, + min_followup = 20 + ) + # IA2 + # The 2nd interim analysis will occur at the later of the following 3 conditions: + # - At least 32 months have passed since the start of the study + # - At least 250 events have occurred + # - At least 10 months after IA1 + # However, if events accumulation is slow, we will wait for a maximum of 34 months. + ia2 <- create_cutting( + planned_calendar_time = 32, + target_event_overall = 200, + max_extension_for_target_event = 34, + min_time_after_previous_analysis = 10 + ) + # FA + # The final analysis will occur at the later of the following 2 conditions: + # - At least 45 months have passed since the start of the study + # - At least 300 events have occurred + fa <- create_cutting( + planned_calendar_time = 45, + target_event_overall = 350 + ) + + return(list(ia1 = ia1, ia2 = ia2, fa = fa)) +} diff --git a/tests/testthat/test-unvalidated-sim_gs_n.R b/tests/testthat/test-unvalidated-sim_gs_n.R index 5e7acadb..c8f11916 100644 --- a/tests/testthat/test-unvalidated-sim_gs_n.R +++ b/tests/testthat/test-unvalidated-sim_gs_n.R @@ -1,73 +1,16 @@ # 2024-02-22: Converted `example("sim_gs_n")` to tests from commit 306de0d # https://github.com/Merck/simtrial/tree/306de0dbe380fdb1e906a59f34bf3871d3ee5312 -# parameters for enrollment -enroll_rampup_duration <- 4 # duration for enrollment ramp up -enroll_duration <- 16 # total enrollment duration -enroll_rate <- gsDesign2::define_enroll_rate( - duration = c(enroll_rampup_duration, - enroll_duration - enroll_rampup_duration), - rate = c(10, 30) -) -# parameters for treatment effect -delay_effect_duration <- 3 # delay treatment effect in months -median_col <- 9 # survival median of the control arm -median_exp <- c(9, 14) # survival median of the experimental arm -dropout_rate <- 0.001 -fail_rate <- gsDesign2::define_fail_rate( - duration = c(delay_effect_duration, 100), - fail_rate = log(2) / median_col, - hr = median_col / median_exp, - dropout_rate = dropout_rate -) -# other related parameters -alpha <- 0.025 # type I error -beta <- 0.1 # type II error -ratio <- 1 # randomization ratio (exp:col) -# Define cuttings of 2 IAs and 1 FA -# IA1 -# The 1st interim analysis will occur at the later of the following 3 conditions: -# - At least 20 months have passed since the start of the study -# - At least 100 events have occurred -# - At least 20 months have elapsed after enrolling 200/400 subjects, with a -# minimum of 20 months follow-up -# However, if events accumulation is slow, we will wait for a maximum of 24 months. -ia1 <- create_cutting( - planned_calendar_time = 20, - target_event_overall = 100, - max_extension_for_target_event = 24, - min_n_overall = 200, - min_followup = 20 -) -# IA2 -# The 2nd interim analysis will occur at the later of the following 3 conditions: -# - At least 32 months have passed since the start of the study -# - At least 250 events have occurred -# - At least 10 months after IA1 -# However, if events accumulation is slow, we will wait for a maximum of 34 months. -ia2 <- create_cutting( - planned_calendar_time = 32, - target_event_overall = 200, - max_extension_for_target_event = 34, - min_time_after_previous_analysis = 10 -) -# FA -# The final analysis will occur at the later of the following 2 conditions: -# - At least 45 months have passed since the start of the study -# - At least 300 events have occurred -fa <- create_cutting( - planned_calendar_time = 45, - target_event_overall = 350 -) +# See helper-sim_gs_n.R for helper functions test_that("Test 1: regular logrank test", { observed <- sim_gs_n( n_sim = 3, sample_size = 400, - enroll_rate = enroll_rate, - fail_rate = fail_rate, + enroll_rate = test_enroll_rate(), + fail_rate = test_fail_rate(), test = wlr, - cutting = list(ia1 = ia1, ia2 = ia2, fa = fa), + cutting = test_cutting(), seed = 2024, weight = fh(rho = 0, gamma = 0) ) @@ -90,10 +33,10 @@ test_that("Test 2: weighted logrank test by FH(0, 0.5)", { observed <- sim_gs_n( n_sim = 3, sample_size = 400, - enroll_rate = enroll_rate, - fail_rate = fail_rate, + enroll_rate = test_enroll_rate(), + fail_rate = test_fail_rate(), test = wlr, - cutting = list(ia1 = ia1, ia2 = ia2, fa = fa), + cutting = test_cutting(), seed = 2024, weight = fh(rho = 0, gamma = 0.5) ) @@ -116,10 +59,10 @@ test_that("Test 3: weighted logrank test by MB(6)", { observed <- sim_gs_n( n_sim = 3, sample_size = 400, - enroll_rate = enroll_rate, - fail_rate = fail_rate, + enroll_rate = test_enroll_rate(), + fail_rate = test_fail_rate(), test = wlr, - cutting = list(ia1 = ia1, ia2 = ia2, fa = fa), + cutting = test_cutting(), seed = 2024, weight = mb(delay = 3) ) @@ -140,10 +83,10 @@ test_that("Test 4: weighted logrank test by early zero (6)", { observed <- sim_gs_n( n_sim = 3, sample_size = 400, - enroll_rate = enroll_rate, - fail_rate = fail_rate, + enroll_rate = test_enroll_rate(), + fail_rate = test_fail_rate(), test = wlr, - cutting = list(ia1 = ia1, ia2 = ia2, fa = fa), + cutting = test_cutting(), seed = 2024, weight = early_zero(6) ) @@ -164,10 +107,10 @@ test_that("Test 5: RMST", { observed <- sim_gs_n( n_sim = 3, sample_size = 400, - enroll_rate = enroll_rate, - fail_rate = fail_rate, + enroll_rate = test_enroll_rate(), + fail_rate = test_fail_rate(), test = rmst, - cutting = list(ia1 = ia1, ia2 = ia2, fa = fa), + cutting = test_cutting(), seed = 2024, tau = 20 ) @@ -203,10 +146,10 @@ test_that("Test 6: maxcombo (FH(0,0) + FH(0, 0.5))", { observed <- sim_gs_n( n_sim = 3, sample_size = 400, - enroll_rate = enroll_rate, - fail_rate = fail_rate, + enroll_rate = test_enroll_rate(), + fail_rate = test_fail_rate(), test = maxcombo, - cutting = list(ia1 = ia1, ia2 = ia2, fa = fa), + cutting = test_cutting(), seed = 2024, rho = c(0, 0), gamma = c(0, 0.5) From b302c3e9eea35b0dd2a579bfb16fafecb730ca50 Mon Sep 17 00:00:00 2001 From: John Blischak Date: Fri, 23 Feb 2024 13:39:48 -0500 Subject: [PATCH 13/13] Document `sim_gs_n()` and `maxcombo()` as experimental --- R/maxcombo.R | 3 +++ R/sim_gs_n.R | 4 ++++ R/wlr_weight.R | 4 ++-- man/fh.Rd | 4 ++-- man/maxcombo.Rd | 3 ++- man/sim_gs_n.Rd | 3 ++- 6 files changed, 15 insertions(+), 6 deletions(-) diff --git a/R/maxcombo.R b/R/maxcombo.R index c36721bc..b9a7f503 100644 --- a/R/maxcombo.R +++ b/R/maxcombo.R @@ -18,6 +18,9 @@ #' Maxcombo test #' +#' WARNING: This experimental function is a work-in-progress. The function +#' arguments will change as we add additional features. +#' #' @param data a tte dataset #' @param rho Numeric vector passed to \code{\link{fh_weight}}. Must be greater #' than or equal to zero. Must be the same length as \code{gamma}. diff --git a/R/sim_gs_n.R b/R/sim_gs_n.R index 6e3c09f2..043ab23c 100644 --- a/R/sim_gs_n.R +++ b/R/sim_gs_n.R @@ -17,6 +17,10 @@ # along with this program. If not, see . #' Simulate group sequential designs with fixed sample size +#' +#' WARNING: This experimental function is a work-in-progress. The function +#' arguments will change as we add additional features. +#' #' @inheritParams sim_fixed_n #' @param test a test function such as \code{\link{wlr}}, #' \code{\link{maxcombo}}, or \code{\link{rmst}}. The simulated data set is diff --git a/R/wlr_weight.R b/R/wlr_weight.R index 92ab253a..f4106407 100644 --- a/R/wlr_weight.R +++ b/R/wlr_weight.R @@ -18,8 +18,8 @@ #' Fleming-Harrington weighting function #' -#' @param rho Non-negative number. \code{rho = 0, gamma = 0} is equavalent to regular logrank test. -#' @param gamma Non-negative number. \code{rho = 0, gamma = 0} is equavalent to regular logrank test. +#' @param rho Non-negative number. \code{rho = 0, gamma = 0} is equivalent to regular logrank test. +#' @param gamma Non-negative number. \code{rho = 0, gamma = 0} is equivalent to regular logrank test. #' #' @export #' @return A list of parameters of the Fleming-Harrington weighting function diff --git a/man/fh.Rd b/man/fh.Rd index 4760a085..9f191609 100644 --- a/man/fh.Rd +++ b/man/fh.Rd @@ -7,9 +7,9 @@ fh(rho = 0, gamma = 0) } \arguments{ -\item{rho}{Non-negative number. \code{rho = 0, gamma = 0} is equavalent to regular logrank test.} +\item{rho}{Non-negative number. \code{rho = 0, gamma = 0} is equivalent to regular logrank test.} -\item{gamma}{Non-negative number. \code{rho = 0, gamma = 0} is equavalent to regular logrank test.} +\item{gamma}{Non-negative number. \code{rho = 0, gamma = 0} is equivalent to regular logrank test.} } \value{ A list of parameters of the Fleming-Harrington weighting function diff --git a/man/maxcombo.Rd b/man/maxcombo.Rd index 28eca247..4159f060 100644 --- a/man/maxcombo.Rd +++ b/man/maxcombo.Rd @@ -19,7 +19,8 @@ greater than or equal to zero. Must be the same length as \code{rho}.} pvalues } \description{ -Maxcombo test +WARNING: This experimental function is a work-in-progress. The function +arguments will change as we add additional features. } \examples{ sim_pw_surv(n = 200) |> diff --git a/man/sim_gs_n.Rd b/man/sim_gs_n.Rd index 9aa3741f..4df396b6 100644 --- a/man/sim_gs_n.Rd +++ b/man/sim_gs_n.Rd @@ -52,7 +52,8 @@ passed as the first positional argument to the test function provided.} a data frame summarizing the simulation ID, analysis date, z statistics or p-values } \description{ -Simulate group sequential designs with fixed sample size +WARNING: This experimental function is a work-in-progress. The function +arguments will change as we add additional features. } \examples{ library(gsDesign2)