diff --git a/DESCRIPTION b/DESCRIPTION index 5f26d104..c90ea708 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.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")), @@ -44,7 +44,6 @@ Imports: future, methods, mvtnorm, - rlang, stats, survival, utils 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/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/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..b9a7f503 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. @@ -18,38 +18,37 @@ #' 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 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 1: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/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 b7463903..043ab23c 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. @@ -16,13 +16,22 @@ # 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 +#' +#' 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 functional call of the test such as \code{wlr()} or \code{maxcombo()} -#' @param cutting a functional call of the cutting for IA(s) and FA, see examples +#' @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 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 @@ -58,12 +67,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: @@ -71,19 +81,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( @@ -91,9 +103,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 +114,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 +126,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 +137,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 +148,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 +159,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, +#' rho = c(0, 0), +#' gamma = c(0, 0.5)) sim_gs_n <- function( # number of simulations n_sim = 1000, @@ -168,18 +185,20 @@ 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 # 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( @@ -195,20 +214,16 @@ 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 - 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]) # 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 @@ -221,3 +236,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/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..fdd65265 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. @@ -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) |> @@ -38,29 +42,35 @@ #' 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) |> - 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) - } else if ("early_period" %in% class(weight)){ + 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) |> - dplyr::summarize( - s = sum(o_minus_e * weight), - v = sum(var_o_minus_e * weight^2), - z = s / sqrt(v)) |> - dplyr::select(z) + 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) } diff --git a/R/wlr_weight.R b/R/wlr_weight.R index ff03e7ba..f4106407 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. @@ -18,17 +18,15 @@ #' 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 #' @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")) } diff --git a/_pkgdown.yml b/_pkgdown.yml index ba44cdad..ac94271d 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: @@ -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: 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/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 35e32c75..4159f060 100644 --- a/man/maxcombo.Rd +++ b/man/maxcombo.Rd @@ -4,26 +4,29 @@ \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 } \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) |> 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 728a7079..4df396b6 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, @@ -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,17 +36,24 @@ 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{cutting}{a list of cutting functions created by +\code{\link{create_cutting}}, 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 +a data frame summarizing the simulation ID, analysis date, z statistics or p-values } \description{ -Simulate group sequantial 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) @@ -80,12 +88,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: @@ -93,19 +102,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( @@ -113,9 +124,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 +135,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 +147,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 +158,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 +169,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 +180,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, + rho = c(0, 0), + gamma = c(0, 0.5)) } 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 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-maxcombo.R b/tests/testthat/test-unvalidated-maxcombo.R new file mode 100644 index 00000000..a69f64b8 --- /dev/null +++ b/tests/testthat/test-unvalidated-maxcombo.R @@ -0,0 +1,21 @@ +# 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(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 new file mode 100644 index 00000000..c8f11916 --- /dev/null +++ b/tests/testthat/test-unvalidated-sim_gs_n.R @@ -0,0 +1,168 @@ +# 2024-02-22: Converted `example("sim_gs_n")` to tests from commit 306de0d +# https://github.com/Merck/simtrial/tree/306de0dbe380fdb1e906a59f34bf3871d3ee5312 + +# 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 = test_enroll_rate(), + fail_rate = test_fail_rate(), + test = wlr, + cutting = test_cutting(), + seed = 2024, + weight = fh(rho = 0, gamma = 0) + ) + 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 = test_enroll_rate(), + fail_rate = test_fail_rate(), + test = wlr, + cutting = test_cutting(), + seed = 2024, + weight = fh(rho = 0, gamma = 0.5) + ) + 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 = test_enroll_rate(), + fail_rate = test_fail_rate(), + test = wlr, + cutting = test_cutting(), + seed = 2024, + weight = mb(delay = 3) + ) + 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 = test_enroll_rate(), + fail_rate = test_fail_rate(), + test = wlr, + cutting = test_cutting(), + seed = 2024, + weight = early_zero(6) + ) + 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 = test_enroll_rate(), + fail_rate = test_fail_rate(), + test = rmst, + cutting = test_cutting(), + seed = 2024, + tau = 20 + ) + 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 = test_enroll_rate(), + fail_rate = test_fail_rate(), + test = maxcombo, + cutting = test_cutting(), + seed = 2024, + rho = c(0, 0), + gamma = c(0, 0.5) + ) + 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) +})