Skip to content

Commit ebb4829

Browse files
Merge pull request #339 from jdblischak/sim-gs-n-ex11
Add bounds to sim_gs_n() output using {data.table}
2 parents 7e656e3 + b0d54e7 commit ebb4829

File tree

3 files changed

+69
-9
lines changed

3 files changed

+69
-9
lines changed

NAMESPACE

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ importFrom(data.table,merge.data.table)
4545
importFrom(data.table,rbindlist)
4646
importFrom(data.table,setDF)
4747
importFrom(data.table,setDT)
48+
importFrom(data.table,setcolorder)
49+
importFrom(data.table,setnames)
4850
importFrom(data.table,setorderv)
4951
importFrom(data.table,uniqueN)
5052
importFrom(doFuture,"%dofuture%")

R/sim_gs_n.R

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
#' @return A data frame summarizing the simulation ID, analysis date,
5454
#' z statistics or p-values.
5555
#'
56-
#' @importFrom data.table rbindlist setDF
56+
#' @importFrom data.table as.data.table dcast rbindlist setcolorder setDF setnames
5757
#'
5858
#' @export
5959
#'
@@ -408,10 +408,13 @@ sim_gs_n <- function(
408408
# Add planned and updated bounds
409409
if (!is.null(original_design) && is_logrank){
410410
# Add planned bounds
411-
ans_1sim <- ans_1sim |>
412-
dplyr::left_join(original_design$bound |> dplyr::filter(bound == "upper") |> dplyr::select(analysis, z) |> dplyr::rename(planed_upper_bound = z)) |>
413-
dplyr::left_join(original_design$bound |> dplyr::filter(bound == "lower") |> dplyr::select(analysis, z) |> dplyr::rename(planed_lower_bound = z))
414-
411+
planned_bounds <- as.data.table(original_design$bound)
412+
planned_bounds <- dcast(planned_bounds, analysis ~ bound, fill = NA, drop = FALSE, value.var = "z")
413+
setnames(planned_bounds, c("analysis", "planned_lower_bound", "planned_upper_bound"))
414+
# workaround for the fact that merge() moves the "by" column to be first
415+
final_column_order <- union(colnames(ans_1sim), colnames(planned_bounds))
416+
ans_1sim <- merge(ans_1sim, planned_bounds, all.x = TRUE, sort = FALSE)
417+
setcolorder(ans_1sim, final_column_order)
415418

416419
# Calculate ustime and lstime
417420
obs_event <- with(event_tbl, tapply(event, analysis, sum, simplify = TRUE))
@@ -437,9 +440,13 @@ sim_gs_n <- function(
437440
lstime = if(all(original_design$bound$bound == "upper")){NULL}else{lstime},
438441
event_tbl = event_tbl)
439442

440-
ans_1sim <- ans_1sim |>
441-
dplyr::left_join(updated_design$bound |> dplyr::filter(bound == "upper") |> dplyr::select(analysis, z) |> dplyr::rename(updated_upper_bound = z)) |>
442-
dplyr::left_join(updated_design$bound |> dplyr::filter(bound == "lower") |> dplyr::select(analysis, z) |> dplyr::rename(updated_lower_bound = z))
443+
updated_bounds <- as.data.table(updated_design$bound)
444+
updated_bounds <- dcast(updated_bounds, analysis ~ bound, fill = NA, drop = FALSE, value.var = "z")
445+
setnames(updated_bounds, c("analysis", "updated_lower_bound", "updated_upper_bound"))
446+
# workaround for the fact that merge() moves the "by" column to be first
447+
final_column_order <- union(colnames(ans_1sim), colnames(updated_bounds))
448+
ans_1sim <- merge(ans_1sim, updated_bounds, all.x = TRUE, sort = FALSE)
449+
setcolorder(ans_1sim, final_column_order)
443450
}
444451

445452
ans_1sim

tests/testthat/test-unvalidated-sim_gs_n.R

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,8 @@ test_that("create_cut() can accept variables as arguments", {
662662
})
663663

664664
test_that("Updating bounds changes the simulation results", {
665+
skip_if_not_installed("gsDesign2")
666+
665667
x <- gsDesign2::gs_design_ahr(analysis_time = 1:3*12) |>
666668
gsDesign2::to_integer()
667669

@@ -702,7 +704,56 @@ test_that("Updating bounds changes the simulation results", {
702704
updated_upper_bound = c(3.870248012128966, 2.3867954048423474, 2.0074221828251764),
703705
updated_lower_bound = c(-1.6671962217546439, 0.9631736579151768, 2.1126105535696467)
704706
)
705-
observed <- run2[, c("planed_upper_bound", "planed_lower_bound",
707+
observed <- run2[, c("planned_upper_bound", "planned_lower_bound",
706708
"updated_upper_bound", "updated_lower_bound")]
707709
expect_equal(observed, expected, ignore_attr = TRUE)
708710
})
711+
712+
test_that("sim_gs_n() can update bounds even when some are missing", {
713+
# https://github.com/Merck/simtrial/issues/335
714+
715+
skip_if_not_installed("gsDesign2")
716+
717+
# futility - IA1; efficacy - IA2 & FA
718+
x <- gsDesign2::gs_design_ahr(
719+
alpha = 0.025,
720+
beta = 0.1,
721+
analysis_time = 1:3*12,
722+
upper = gsDesign2::gs_spending_bound,
723+
upar = list(sf = gsDesign::sfLDOF, total_spend = 0.025),
724+
test_upper = c(FALSE, TRUE, TRUE),
725+
lower = gsDesign2::gs_spending_bound,
726+
lpar = list(sf = gsDesign::sfHSD, param = -4, total_spend = 0.01),
727+
test_lower = c(TRUE, FALSE, FALSE)
728+
) |> gsDesign2::to_integer()
729+
730+
set.seed(1)
731+
observed <- sim_gs_n(
732+
n_sim = 1,
733+
sample_size = max(x$analysis$n),
734+
enroll_rate = x$enroll_rate,
735+
fail_rate = x$fail_rate,
736+
test = wlr,
737+
weight = fh(rho = 0, gamma = 0),
738+
cut = list(ia1 = create_cut(planned_calendar_time = x$analysis$time[1]),
739+
ia2 = create_cut(planned_calendar_time = x$analysis$time[2]),
740+
fa = create_cut(planned_calendar_time = x$analysis$time[3])),
741+
original_design = x)
742+
743+
expect_equal(
744+
observed$planned_upper_bound,
745+
c(NA, 2.35835648246416, 2.00932773528063)
746+
)
747+
expect_equal(
748+
observed$planned_lower_bound,
749+
c(-2.31975897600847, NA, NA)
750+
)
751+
expect_equal(
752+
observed$updated_upper_bound,
753+
c(Inf, 2.46416041021134, 1.99094670148633)
754+
)
755+
expect_equal(
756+
observed$updated_lower_bound,
757+
c(-2.72847356838699, -Inf, -Inf)
758+
)
759+
})

0 commit comments

Comments
 (0)