From 8be540cbe38ba0e5bd8a9e5d73cfe30758ff4547 Mon Sep 17 00:00:00 2001 From: Yihui Xie Date: Tue, 14 Oct 2025 21:50:37 -0500 Subject: [PATCH 1/2] support design objects containing spending functions specified as character strings in `text_summary()` --- R/gs_spending_bound.R | 7 +------ R/gs_spending_combo.R | 3 ++- R/text_summary.R | 6 ++++-- R/utils.R | 10 ++++++++++ 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/R/gs_spending_bound.R b/R/gs_spending_bound.R index 743ad8ce..854e8b06 100644 --- a/R/gs_spending_bound.R +++ b/R/gs_spending_bound.R @@ -121,12 +121,7 @@ gs_spending_bound <- function(k = 1, } # Compute cumulative spending at each analyses ---- - if (!is.function(sf <- par$sf)) sf <- tryCatch(match.fun(sf), error = function(e) { - # in case gsDesign is not attached (i.e. library(gsDesign)) or the spending - # function is not imported into gsDesign2 from gsDesign, we will get it from - # gsDesign's namespace - getExportedValue('gsDesign', sf) - }) + sf <- get_sf(par$sf) spend <- sf(alpha = par$total_spend, t = timing, param = par$param)$spend # Compute incremental spending at each analyses ---- diff --git a/R/gs_spending_combo.R b/R/gs_spending_combo.R index 0160aae9..1d3ce69f 100644 --- a/R/gs_spending_combo.R +++ b/R/gs_spending_combo.R @@ -104,5 +104,6 @@ #' par <- list(sf = gsDesign::sfLDOF, total_spend = 0.2) #' gs_spending_combo(par, info = 1:3 / 3) gs_spending_combo <- function(par = NULL, info = NULL) { - par$sf(alpha = par$total_spend, t = info, param = par$param)$spend + sf <- get_sf(par$sf) + sf(alpha = par$total_spend, t = info, param = par$param)$spend } diff --git a/R/text_summary.R b/R/text_summary.R index 52a12891..e63ad599 100644 --- a/R/text_summary.R +++ b/R/text_summary.R @@ -189,7 +189,8 @@ text_summary <- function(x, information = FALSE, time_unit = "months") { } analysis_seq <- c(paste("IA", 1:(n_analysis - 1), sep = ""), "FA") - upper_text <- x$input$upar$sf(alpha = x$input$upar$total_spend, t = x$analysis$info_frac, param = x$input$upar$param) + sfu <- get_sf(x$input$upar$sf) + upper_text <- sfu(alpha = x$input$upar$total_spend, t = x$analysis$info_frac, param = x$input$upar$param) upper_tested <- if (!all(x$input$test_upper)) { paste(", tested at", paste("tested at", paste(analysis_seq[x$input$test_upper], collapse = ", "))) } @@ -206,7 +207,8 @@ text_summary <- function(x, information = FALSE, time_unit = "months") { } if (identical(x$input$lower, gs_spending_bound)) { - lower_text <- x$input$lpar$sf(alpha = x$input$lpar$total_spend, t = x$analysis$info_frac, param = x$input$lpar$param) + sfl <- get_sf(x$input$lpar$sf) + lower_text <- sfl(alpha = x$input$lpar$total_spend, t = x$analysis$info_frac, param = x$input$lpar$param) out <- paste(out, " Futility bounds derived using a ", summary(lower_text), lower_tested, ".", sep = "") } else if (identical(x$input$lower, gs_b)) { out <- paste(out, " Futility bounds is fixed as ", paste0(x$input$lpar, collapse = ", ") , lower_tested, ".", sep = "") diff --git a/R/utils.R b/R/utils.R index f8991b56..fc7f8a12 100644 --- a/R/utils.R +++ b/R/utils.R @@ -106,3 +106,13 @@ prune_hash <- function(h, size = 2^23) { # Require exact matching by default when retrieving attributes attr = function(...) base::attr(..., exact = TRUE) + +# get the spending function if it's specified as a string +get_sf <- function(f) { + if (is.function(f)) f else tryCatch(match.fun(f), error = function(e) { + # in case gsDesign is not attached (i.e. library(gsDesign)) or the spending + # function is not imported into gsDesign2 from gsDesign, we will get it from + # gsDesign's namespace + getExportedValue('gsDesign', f) + }) +} From d0a3d424b6a4a944fcd8c82e141d7c2fdfe1fdac Mon Sep 17 00:00:00 2001 From: Yihui Xie Date: Tue, 14 Oct 2025 21:56:16 -0500 Subject: [PATCH 2/2] add news [ci skip] --- DESCRIPTION | 2 +- NEWS.md | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 327cc9ea..137e0e40 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: gsDesign2 Title: Group Sequential Design with Non-Constant Effect -Version: 1.1.6 +Version: 1.1.6.1 Authors@R: c( person("Keaven", "Anderson", email = "keaven_anderson@merck.com", role = c("aut")), person("Yujie", "Zhao", email = "yujie.zhao@merck.com", role = c("aut", "cre")), diff --git a/NEWS.md b/NEWS.md index e8e8196a..1be5e207 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,11 +1,19 @@ +# gsDesign2 1.1.7 + +## User interface improvements + +- `text_summary()` supports design objects with spending functions specified as character strings (#587, thanks to @yihui). + # gsDesign2 1.1.6 ## Statistical improvements + - The `gs_power_wlr()` function now includes an `h1_spending` argument, allowing users to specify a spending under the alternative hypothesis (#565, thanks to @LittleBeannie). - The following functions now support `info_scale` argument: `fixed_design_ahr()`, `fixed_design_fh()`, `fixed_design_mb()`, and `fixed_design_rd()` (#571, thanks to @LittleBeannie). - Functions for fixed designs with integer sample sizes now return the average HR in their output, providing a more complete summary of the design characteristics. (#572, thanks to @LittleBeannie). ## Documentation + - The documentation for `gs_design_npe()` and `gs_power_npe()` has been consolidated into a single topic for improved clarity and easier navigation. (#567, thanks to @LittleBeannie). - The package codebase has been updated to use the native R pipe (`|>`) exclusively, removing the `magrittr` dependency and aligning with modern R practices. (#577, thanks to @jdblischak).