From 76bec4147af6cc064b1e22583ac26940cdf7e83d Mon Sep 17 00:00:00 2001 From: John Blischak Date: Thu, 27 Jun 2024 13:32:05 -0400 Subject: [PATCH 1/5] Refactor code to set default col_vars and col_decimals --- R/summary.R | 69 ++++++++++--------------- tests/testthat/test-developer-summary.R | 21 +++++++- 2 files changed, 46 insertions(+), 44 deletions(-) diff --git a/R/summary.R b/R/summary.R index 5c9971c3..2f42b6c7 100644 --- a/R/summary.R +++ b/R/summary.R @@ -315,50 +315,35 @@ summary.gs_design <- function(object, # Prepare the columns decimals ---- if (method == "ahr") { - if (is.null(col_vars) && is.null(col_decimals)) { - x_decimals <- tibble::tibble( - col_vars = c("analysis", "bound", "z", "~hr at bound", "nominal p", "Alternate hypothesis", "Null hypothesis"), - col_decimals = c(NA, NA, 2, 4, 4, 4, 4) - ) - } else { - x_decimals <- tibble::tibble(col_vars = col_vars, col_decimals = col_decimals) - } - } - - if (method == "wlr") { - if (is.null(col_vars) && is.null(col_decimals)) { - x_decimals <- tibble::tibble( - col_vars = c("analysis", "bound", "z", "~whr at bound", "nominal p", "Alternate hypothesis", "Null hypothesis"), - col_decimals = c(NA, NA, 2, 4, 4, 4, 4) - ) - } else { - x_decimals <- tibble::tibble(col_vars = col_vars, col_decimals = col_decimals) - } - } - - if (method == "combo") { - if (is.null(col_vars) && is.null(col_decimals)) { - x_decimals <- tibble::tibble( - col_vars = c("analysis", "bound", "z", "nominal p", "Alternate hypothesis", "Null hypothesis"), - col_decimals = c(NA, NA, 2, 4, 4, 4) - ) - } else { - x_decimals <- tibble::tibble(col_vars = col_vars, col_decimals = col_decimals) - } + col_vars_default <- c("analysis", "bound", "z", "~hr at bound", "nominal p", + "Alternate hypothesis", "Null hypothesis") + col_decimals_default <- c(NA, NA, 2, 4, 4, 4, 4) + } else if (method == "wlr") { + col_vars_default <- c("analysis", "bound", "z", "~whr at bound", "nominal p", + "Alternate hypothesis", "Null hypothesis") + col_decimals_default <- c(NA, NA, 2, 4, 4, 4, 4) + } else if (method == "combo") { + col_vars_default <- c("analysis", "bound", "z", "nominal p", + "Alternate hypothesis", "Null hypothesis") + col_decimals_default <- c(NA, NA, 2, 4, 4, 4) + } else if (method == "rd") { + col_vars_default <- c("analysis", "bound", "z", "~risk difference at bound", + "nominal p", "Alternate hypothesis", "Null hypothesis") + col_decimals_default <- c(NA, NA, 2, 4, 4, 4, 4) + } else { + stop("Invalid method: ", method) } - if (method == "rd") { - if (is.null(col_vars) && is.null(col_decimals)) { - x_decimals <- tibble::tibble( - col_vars = c( - "analysis", "bound", "z", "~risk difference at bound", - "nominal p", "Alternate hypothesis", "Null hypothesis" - ), - col_decimals = c(NA, NA, 2, 4, 4, 4, 4) - ) - } else { - x_decimals <- tibble::tibble(col_vars = col_vars, col_decimals = col_decimals) - } + if (is.null(col_vars) && is.null(col_decimals)) { + x_decimals <- tibble::tibble( + col_vars = col_vars_default, + col_decimals = col_decimals_default + ) + } else { + x_decimals <- tibble::tibble( + col_vars = col_vars, + col_decimals = col_decimals + ) } # "bound" is a required column diff --git a/tests/testthat/test-developer-summary.R b/tests/testthat/test-developer-summary.R index fc0ace99..1f6ec241 100644 --- a/tests/testthat/test-developer-summary.R +++ b/tests/testthat/test-developer-summary.R @@ -150,7 +150,6 @@ test_that("The column 'Bound' is always included in summary.gs_design() output", expect_true("Bound" %in% colnames(observed)) }) - test_that("The full alpha is correctly carried over", { a_level <- 0.02 x <- gs_power_ahr( @@ -166,4 +165,22 @@ test_that("The full alpha is correctly carried over", { observed <- summary(x) expect_equal(attributes(observed)$full_alpha, a_level) -}) \ No newline at end of file +}) + +test_that("col_vars and col_decimals can be passed 1:1", { + x <- gs_design_ahr() + + observed <- summary( + x, + col_vars = c("z", "~hr at bound", "nominal p", "Alternate hypothesis"), + col_decimals = c(0, 0, 0, 0) + ) + + columns <- c("Z", "~HR at bound", "Nominal p", "Alternate hypothesis") + for (col in columns) { + expect_equal(observed[1, col, drop = TRUE], as.integer(observed[1, col])) + } + + # Purposefully omitted "Null hypothesis" from col_vars above + expect_false("Null hypothesis" %in% colnames(observed)) +}) From ef30b1b79ee5433c03d976b12b08b8a7c7058f6e Mon Sep 17 00:00:00 2001 From: John Blischak Date: Thu, 27 Jun 2024 15:05:19 -0400 Subject: [PATCH 2/5] Enable passing named vector of col_decimals to summary.gs_design() --- DESCRIPTION | 2 +- R/summary.R | 59 +++++++++--- man/summary.Rd | 5 +- tests/testthat/test-developer-summary.R | 123 ++++++++++++++++++++++-- 4 files changed, 163 insertions(+), 26 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 569114a7..43e6e35a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: gsDesign2 Title: Group Sequential Design with Non-Constant Effect -Version: 1.1.2.13 +Version: 1.1.2.14 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/summary.R b/R/summary.R index 2f42b6c7..1973c42e 100644 --- a/R/summary.R +++ b/R/summary.R @@ -152,6 +152,9 @@ summary.fixed_design <- function(object, ...) { #' variables you want to be displayed differently than the defaults. #' @param col_vars The variables to be displayed. #' @param col_decimals The decimals to be displayed for the displayed variables in `col_vars`. +#' If the vector is unnamed, it must match the length of `col_vars`. If the +#' vector is named, you only have to specify the number of digits for the +#' columns you want to be displayed differently than the defaults. #' @param bound_names Names for bounds; default is `c("Efficacy", "Futility")`. #' #' @importFrom dplyr all_of @@ -334,26 +337,54 @@ summary.gs_design <- function(object, stop("Invalid method: ", method) } + # Filter columns and update decimal places + names(col_decimals_default) <- col_vars_default if (is.null(col_vars) && is.null(col_decimals)) { - x_decimals <- tibble::tibble( - col_vars = col_vars_default, - col_decimals = col_decimals_default - ) - } else { - x_decimals <- tibble::tibble( - col_vars = col_vars, - col_decimals = col_decimals - ) + # Use default values + col_vars <- col_vars_default + col_decimals <- col_decimals_default + } else if (!is.null(col_vars) && is.null(col_decimals)) { + # Only drop/rearrange variables + col_decimals <- col_decimals_default[ + match(col_vars, names(col_decimals_default)) + ] + } else if (is.null(col_vars) && !is.null(col_decimals)) { + # Only update decimals - must be named vector + if (is.null(names(col_decimals))) { + stop("summary: col_decimals must be a named vector if col_vars is not provided") + } + col_vars <- col_vars_default + col_decimals_tmp <- col_decimals_default + col_decimals_tmp[names(col_decimals)] <- col_decimals + col_decimals <- col_decimals_tmp + } else if (!is.null(col_vars) && !is.null(col_decimals)) { + # Update variables and decimals + if (is.null(names(col_decimals))) { + # vectors must be same length if col_decimals is unnamed + if (length(col_vars) != length(col_decimals)) { + stop("summary: please input col_vars and col_decimals in pairs!") + } + } else { + col_decimals_tmp <- col_decimals_default + col_decimals_tmp[names(col_decimals)] <- col_decimals + col_decimals <- col_decimals_tmp + col_decimals <- col_decimals[ + match(col_vars, names(col_decimals)) + ] + } } # "bound" is a required column - if (!"bound" %in% x_decimals$col_vars) { - x_decimals <- rbind( - tibble::tibble(col_vars = "bound", col_decimals = NA), - x_decimals - ) + if (!"bound" %in% col_vars) { + col_vars <- c("bound", col_vars) + col_decimals <- c(NA, col_decimals) } + x_decimals <- tibble::tibble( + col_vars = col_vars, + col_decimals = col_decimals + ) + # Prepare the analysis summary row ---- # get the # (1) analysis variables to be displayed on the header diff --git a/man/summary.Rd b/man/summary.Rd index 0322b93d..46d10ca0 100644 --- a/man/summary.Rd +++ b/man/summary.Rd @@ -31,7 +31,10 @@ variables you want to be displayed differently than the defaults.} \item{col_vars}{The variables to be displayed.} -\item{col_decimals}{The decimals to be displayed for the displayed variables in \code{col_vars}.} +\item{col_decimals}{The decimals to be displayed for the displayed variables in \code{col_vars}. +If the vector is unnamed, it must match the length of \code{col_vars}. If the +vector is named, you only have to specify the number of digits for the +columns you want to be displayed differently than the defaults.} \item{bound_names}{Names for bounds; default is \code{c("Efficacy", "Futility")}.} } diff --git a/tests/testthat/test-developer-summary.R b/tests/testthat/test-developer-summary.R index 1f6ec241..55400cb1 100644 --- a/tests/testthat/test-developer-summary.R +++ b/tests/testthat/test-developer-summary.R @@ -167,20 +167,123 @@ test_that("The full alpha is correctly carried over", { expect_equal(attributes(observed)$full_alpha, a_level) }) -test_that("col_vars and col_decimals can be passed 1:1", { +# Maintain previous behavior +test_that("summary.gs_design() accepts same-length vectors for col_vars and col_decimals", { x <- gs_design_ahr() - observed <- summary( + # default decimals + x_sum <- summary(x) + observed <- as.data.frame(x_sum)[, -1:-2] + expected <- data.frame( + Z = 1.96, + `~HR at bound` = 0.795, + `Nominal p` = 0.025, + `Alternate hypothesis` = 0.9, + `Null hypothesis` = 0.025, + check.names = FALSE + ) + expect_equal(observed, expected) + + # specify the decimals for each variable + x_sum <- summary( x, - col_vars = c("z", "~hr at bound", "nominal p", "Alternate hypothesis"), - col_decimals = c(0, 0, 0, 0) + col_vars = c("z", "~hr at bound", "nominal p", "Alternate hypothesis", "Null hypothesis"), + col_decimals = c(0, 0, 0, 0, 0) + ) + observed <- as.data.frame(x_sum)[, -1:-2] + expected <- data.frame( + Z = 2, + `~HR at bound` = 1, + `Nominal p` = 0, + `Alternate hypothesis` = 1, + `Null hypothesis` = 0, + check.names = FALSE ) + expect_equal(observed, expected) - columns <- c("Z", "~HR at bound", "Nominal p", "Alternate hypothesis") - for (col in columns) { - expect_equal(observed[1, col, drop = TRUE], as.integer(observed[1, col])) - } + # Drop variables and also specify the decimals + x_sum <- summary( + x, + col_vars = c("nominal p", "Null hypothesis"), + col_decimals = c(0, 0) + ) + observed <- as.data.frame(x_sum)[, -1:-2] + expected <- data.frame(`Nominal p` = 0, `Null hypothesis` = 0, check.names = FALSE) + expect_equal(observed, expected) - # Purposefully omitted "Null hypothesis" from col_vars above - expect_false("Null hypothesis" %in% colnames(observed)) + # Rearrange variables + x_sum <- summary( + x, + col_vars = c("Null hypothesis", "Alternate hypothesis", "nominal p", "~hr at bound", "z"), + col_decimals = c(0, 0, 0, 0, 0) + ) + observed <- as.data.frame(x_sum)[, -1:-2] + expected <- data.frame( + `Null hypothesis` = 0, + `Alternate hypothesis` = 1, + `Nominal p` = 0, + `~HR at bound` = 1, + Z = 2, + check.names = FALSE + ) + expect_equal(observed, expected) + + # Throw error if unnamed col_decimals does not match length of col_vars + expect_error( + summary( + x, + col_vars = c("Null hypothesis", "Alternate hypothesis", "nominal p"), + col_decimals = c(0, 0), + ), + "summary: please input col_vars and col_decimals in pairs!" + ) +}) + +test_that("summary.gs_design() accepts a named vector for col_decimals", { + x <- gs_design_ahr() + + # Specify decimals + x_sum <- summary(x, col_decimals = c(z = 0, `nominal p` = 0)) + observed <- as.data.frame(x_sum)[, -1:-2] + expected <- data.frame( + Z = 2, + `~HR at bound` = 0.795, + `Nominal p` = 0, + `Alternate hypothesis` = 0.9, + `Null hypothesis` = 0.025, + check.names = FALSE + ) + expect_equal(observed, expected) + + # Specify decimals and also drop some variables + x_sum <- summary( + x, + col_vars = c("z", "nominal p", "Null hypothesis"), + col_decimals = c(z = 0, `nominal p` = 0) + ) + observed <- as.data.frame(x_sum)[, -1:-2] + expected <- data.frame(Z = 2, `Nominal p` = 0, `Null hypothesis` = 0.025, check.names = FALSE) + expect_equal(observed, expected) + + # Specify decimals and rearrange some variables + x_sum <- summary( + x, + col_vars = c("Null hypothesis", "nominal p", "z"), + col_decimals = c(z = 0, `nominal p` = 0) + ) + observed <- as.data.frame(x_sum)[, -1:-2] + expected <- data.frame(`Null hypothesis` = 0.025, `Nominal p` = 0, Z = 2, check.names = FALSE) + expect_equal(observed, expected) + + # Only drop variables + x_sum <- summary(x,col_vars = c("z", "nominal p", "Null hypothesis")) + observed <- as.data.frame(x_sum)[, -1:-2] + expected <- data.frame(Z = 1.96, `Nominal p` = 0.025, `Null hypothesis` = 0.025, check.names = FALSE) + expect_equal(observed, expected) + + # Throw error is col_decimals is unnamed + expect_error( + summary(x, col_decimals = c(4, 4)), + "summary: col_decimals must be a named vector if col_vars is not provided" + ) }) From c2f4cfd86a451e86df7205285952bce27f33607c Mon Sep 17 00:00:00 2001 From: John Blischak Date: Thu, 27 Jun 2024 15:17:50 -0400 Subject: [PATCH 3/5] Run styler on updates to summary.gs_design() --- R/summary.R | 24 ++++++++++++++++-------- tests/testthat/test-developer-summary.R | 18 +++++++++--------- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/R/summary.R b/R/summary.R index 1973c42e..ce533874 100644 --- a/R/summary.R +++ b/R/summary.R @@ -318,20 +318,28 @@ summary.gs_design <- function(object, # Prepare the columns decimals ---- if (method == "ahr") { - col_vars_default <- c("analysis", "bound", "z", "~hr at bound", "nominal p", - "Alternate hypothesis", "Null hypothesis") + col_vars_default <- c( + "analysis", "bound", "z", "~hr at bound", "nominal p", + "Alternate hypothesis", "Null hypothesis" + ) col_decimals_default <- c(NA, NA, 2, 4, 4, 4, 4) } else if (method == "wlr") { - col_vars_default <- c("analysis", "bound", "z", "~whr at bound", "nominal p", - "Alternate hypothesis", "Null hypothesis") + col_vars_default <- c( + "analysis", "bound", "z", "~whr at bound", "nominal p", + "Alternate hypothesis", "Null hypothesis" + ) col_decimals_default <- c(NA, NA, 2, 4, 4, 4, 4) } else if (method == "combo") { - col_vars_default <- c("analysis", "bound", "z", "nominal p", - "Alternate hypothesis", "Null hypothesis") + col_vars_default <- c( + "analysis", "bound", "z", "nominal p", + "Alternate hypothesis", "Null hypothesis" + ) col_decimals_default <- c(NA, NA, 2, 4, 4, 4) } else if (method == "rd") { - col_vars_default <- c("analysis", "bound", "z", "~risk difference at bound", - "nominal p", "Alternate hypothesis", "Null hypothesis") + col_vars_default <- c( + "analysis", "bound", "z", "~risk difference at bound", + "nominal p", "Alternate hypothesis", "Null hypothesis" + ) col_decimals_default <- c(NA, NA, 2, 4, 4, 4, 4) } else { stop("Invalid method: ", method) diff --git a/tests/testthat/test-developer-summary.R b/tests/testthat/test-developer-summary.R index 55400cb1..5c765ce9 100644 --- a/tests/testthat/test-developer-summary.R +++ b/tests/testthat/test-developer-summary.R @@ -257,26 +257,26 @@ test_that("summary.gs_design() accepts a named vector for col_decimals", { # Specify decimals and also drop some variables x_sum <- summary( - x, - col_vars = c("z", "nominal p", "Null hypothesis"), - col_decimals = c(z = 0, `nominal p` = 0) - ) + x, + col_vars = c("z", "nominal p", "Null hypothesis"), + col_decimals = c(z = 0, `nominal p` = 0) + ) observed <- as.data.frame(x_sum)[, -1:-2] expected <- data.frame(Z = 2, `Nominal p` = 0, `Null hypothesis` = 0.025, check.names = FALSE) expect_equal(observed, expected) # Specify decimals and rearrange some variables x_sum <- summary( - x, - col_vars = c("Null hypothesis", "nominal p", "z"), - col_decimals = c(z = 0, `nominal p` = 0) - ) + x, + col_vars = c("Null hypothesis", "nominal p", "z"), + col_decimals = c(z = 0, `nominal p` = 0) + ) observed <- as.data.frame(x_sum)[, -1:-2] expected <- data.frame(`Null hypothesis` = 0.025, `Nominal p` = 0, Z = 2, check.names = FALSE) expect_equal(observed, expected) # Only drop variables - x_sum <- summary(x,col_vars = c("z", "nominal p", "Null hypothesis")) + x_sum <- summary(x, col_vars = c("z", "nominal p", "Null hypothesis")) observed <- as.data.frame(x_sum)[, -1:-2] expected <- data.frame(Z = 1.96, `Nominal p` = 0.025, `Null hypothesis` = 0.025, check.names = FALSE) expect_equal(observed, expected) From ecd686cee124ecddddc0cec8c0d5f1503db10df6 Mon Sep 17 00:00:00 2001 From: John Blischak Date: Thu, 27 Jun 2024 15:24:36 -0400 Subject: [PATCH 4/5] Update vignette to use new named col_decimals --- vignettes/articles/story-update-boundary.Rmd | 32 +++----------------- 1 file changed, 4 insertions(+), 28 deletions(-) diff --git a/vignettes/articles/story-update-boundary.Rmd b/vignettes/articles/story-update-boundary.Rmd index 2a926f87..656ea529 100644 --- a/vignettes/articles/story-update-boundary.Rmd +++ b/vignettes/articles/story-update-boundary.Rmd @@ -131,13 +131,7 @@ gs_update_ahr( x = x, alpha = 0.025 ) |> - summary( - col_vars = c( - "analysis", "bound", "z", "~hr at bound", - "nominal p", "Alternate hypothesis", "Null hypothesis" - ), - col_decimals = c(NA, NA, 4, 4, 4, 4, 4) - ) |> + summary(col_decimals = c(z = 4)) |> as_gt(title = "Updated design", subtitle = "For alternate alpha = 0.025") ``` @@ -184,13 +178,7 @@ gs_update_ahr( ustime = ustime, observed_data = list(observed_data_ia, observed_data_fa) ) |> - summary( - col_vars = c( - "analysis", "bound", "z", "~hr at bound", - "nominal p", "Alternate hypothesis", "Null hypothesis" - ), - col_decimals = c(NA, NA, 4, 4, 4, 4, 4) - ) |> + summary(col_decimals = c(z = 4)) |> as_gt(title = "Updated design", subtitle = paste0("With observed ", sum(observed_data_ia$event), " events at IA and ", sum(observed_data_fa$event), @@ -267,13 +255,7 @@ gs_update_ahr( x = x, alpha = 0.025 ) |> - summary( - col_vars = c( - "analysis", "bound", "z", "~hr at bound", - "nominal p", "Alternate hypothesis", "Null hypothesis" - ), - col_decimals = c(NA, NA, 4, 4, 4, 4, 4) - ) |> + summary(col_decimals = c(z = 4)) |> as_gt(title = "Updated design", subtitle = "For alpha = 0.025") ``` @@ -294,13 +276,7 @@ gs_update_ahr( lstime = ustime, observed_data = list(observed_data_ia, observed_data_fa) ) |> - summary( - col_vars = c( - "analysis", "bound", "z", "~hr at bound", - "nominal p", "Alternate hypothesis", "Null hypothesis" - ), - col_decimals = c(NA, NA, 4, 4, 4, 4, 4) - ) |> + summary(col_decimals = c(z = 4)) |> as_gt(title = "Updated design", subtitle = paste0("With observed ", sum(observed_data_ia$event), " events at IA and ", sum(observed_data_fa$event), From 2bced60594e047e2399f400b2b81867c81290279 Mon Sep 17 00:00:00 2001 From: John Blischak Date: Fri, 28 Jun 2024 11:26:27 -0400 Subject: [PATCH 5/5] Add examples of col_vars and col_decimals --- R/summary.R | 10 ++++++++++ man/summary.Rd | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/R/summary.R b/R/summary.R index ce533874..55b24938 100644 --- a/R/summary.R +++ b/R/summary.R @@ -243,6 +243,16 @@ summary.fixed_design <- function(object, ...) { #' #' # Customize the variables to be summarized for each analysis #' x_ahr %>% summary(analysis_vars = c("n", "event"), analysis_decimals = c(1, 1)) +#' +#' # Customize the digits for the columns +#' x_ahr %>% summary(col_decimals = c(z = 4)) +#' +#' # Customize the columns to display +#' x_ahr %>% summary(col_vars = c("z", "~hr at bound", "nominal p")) +#' +#' # Customize columns and digits +#' x_ahr %>% summary(col_vars = c("z", "~hr at bound", "nominal p"), +#' col_decimals = c(4, 2, 2)) #' } #' #' # Example 2 ---- diff --git a/man/summary.Rd b/man/summary.Rd index 46d10ca0..5e814a0e 100644 --- a/man/summary.Rd +++ b/man/summary.Rd @@ -175,6 +175,16 @@ x_ahr \%>\% summary(bound_names = c("A is better", "B is better")) # Customize the variables to be summarized for each analysis x_ahr \%>\% summary(analysis_vars = c("n", "event"), analysis_decimals = c(1, 1)) + +# Customize the digits for the columns +x_ahr \%>\% summary(col_decimals = c(z = 4)) + +# Customize the columns to display +x_ahr \%>\% summary(col_vars = c("z", "~hr at bound", "nominal p")) + +# Customize columns and digits +x_ahr \%>\% summary(col_vars = c("z", "~hr at bound", "nominal p"), + col_decimals = c(4, 2, 2)) } # Example 2 ----