From 63e3fdba4ed1b3af5ad41dc7e3b2daac7d59ae4a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 17:27:19 +0000 Subject: [PATCH 1/5] Initial plan From 4aad9b3f48562ea756c227f1a4b13476cf355d40 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 17:31:08 +0000 Subject: [PATCH 2/5] Expose digits argument in h_tbl_median_surv and control_surv_med_annot Co-authored-by: shajoezhu <3692541+shajoezhu@users.noreply.github.com> --- R/g_km.R | 6 +++++- R/h_km.R | 18 +++++++++++++----- man/control_annot.Rd | 12 +++++++++++- man/h_tbl_median_surv.Rd | 5 ++++- tests/testthat/test-h_km.R | 14 ++++++++++++++ 5 files changed, 47 insertions(+), 8 deletions(-) diff --git a/R/g_km.R b/R/g_km.R index 3fab8a8ddf..e5f189ac06 100644 --- a/R/g_km.R +++ b/R/g_km.R @@ -536,7 +536,11 @@ g_km <- function(df, # add median survival time annotation table if (annot_surv_med) { - surv_med_tbl <- h_tbl_median_surv(fit_km = fit_km, armval = armval) + surv_med_tbl <- h_tbl_median_surv( + fit_km = fit_km, + armval = armval, + digits = control_annot_surv_med[["digits"]] + ) bg_fill <- if (isTRUE(control_annot_surv_med[["fill"]])) "#00000020" else control_annot_surv_med[["fill"]] gg_surv_med <- df2gg(surv_med_tbl, font_size = font_size, colwidths = c(1, 1, 2), bg_fill = bg_fill) + diff --git a/R/h_km.R b/R/h_km.R index bc7dd6b06d..480285acfb 100644 --- a/R/h_km.R +++ b/R/h_km.R @@ -23,17 +23,21 @@ NULL #' table can be added in [g_km()] by setting `annot_surv_med=TRUE`, and can be configured using the #' `control_surv_med_annot()` function by setting it as the `control_annot_surv_med` argument. #' +#' @param digits (`integer(1)`)\cr number of significant digits to use for rounding the +#' median survival time estimates and confidence interval values in the annotation table. Defaults to `4`. +#' #' @examples #' control_surv_med_annot() #' #' @export -control_surv_med_annot <- function(x = 0.8, y = 0.85, w = 0.32, h = 0.16, fill = TRUE) { +control_surv_med_annot <- function(x = 0.8, y = 0.85, w = 0.32, h = 0.16, fill = TRUE, digits = 4) { assert_proportion_value(x) assert_proportion_value(y) assert_proportion_value(w) assert_proportion_value(h) + checkmate::assert_int(digits, lower = 1) - list(x = x, y = y, w = w, h = h, fill = fill) + list(x = x, y = y, w = w, h = h, fill = fill, digits = digits) } #' @describeIn control_annot Control function for formatting the Cox-PH annotation table. This annotation table can be @@ -117,6 +121,8 @@ h_xticks <- function(data, xticks = NULL, max_time = NULL) { #' Transform a survival fit to a table with groups in rows characterized by N, median and confidence interval. #' #' @inheritParams h_data_plot +#' @param digits (`integer(1)`)\cr number of significant digits to use for rounding the +#' median survival time estimates and confidence interval values. Defaults to `4`. #' #' @return A summary table with statistics `N`, `Median`, and `XX% CI` (`XX` taken from `fit_km`). #' @@ -132,7 +138,7 @@ h_xticks <- function(data, xticks = NULL, max_time = NULL) { #' h_tbl_median_surv(fit_km = fit) #' #' @export -h_tbl_median_surv <- function(fit_km, armval = "All") { +h_tbl_median_surv <- function(fit_km, armval = "All", digits = 4) { y <- if (is.null(fit_km$strata)) { as.data.frame(t(summary(fit_km)$table), row.names = armval) } else { @@ -141,11 +147,13 @@ h_tbl_median_surv <- function(fit_km, armval = "All") { rownames(tbl) <- matrix(unlist(rownames_lst), ncol = 2, byrow = TRUE)[, 2] as.data.frame(tbl) } + checkmate::assert_int(digits, lower = 1) + conf.int <- summary(fit_km)$conf.int # nolint y$records <- round(y$records) - y$median <- signif(y$median, 4) + y$median <- signif(y$median, digits) y$`CI` <- paste0( - "(", signif(y[[paste0(conf.int, "LCL")]], 4), ", ", signif(y[[paste0(conf.int, "UCL")]], 4), ")" + "(", signif(y[[paste0(conf.int, "LCL")]], digits), ", ", signif(y[[paste0(conf.int, "UCL")]], digits), ")" ) stats::setNames( y[c("records", "median", "CI")], diff --git a/man/control_annot.Rd b/man/control_annot.Rd index d6c7c5354f..fd3ae5fadd 100644 --- a/man/control_annot.Rd +++ b/man/control_annot.Rd @@ -6,7 +6,14 @@ \alias{control_coxph_annot} \title{Control functions for Kaplan-Meier plot annotation tables} \usage{ -control_surv_med_annot(x = 0.8, y = 0.85, w = 0.32, h = 0.16, fill = TRUE) +control_surv_med_annot( + x = 0.8, + y = 0.85, + w = 0.32, + h = 0.16, + fill = TRUE, + digits = 4 +) control_coxph_annot( x = 0.29, @@ -29,6 +36,9 @@ control_coxph_annot( \item{fill}{(\code{flag} or \code{character})\cr whether the annotation table should have a background fill color. Can also be a color code to use as the background fill color. If \code{TRUE}, color code defaults to \code{"#00000020"}.} +\item{digits}{(\code{integer(1)})\cr number of significant digits to use for rounding the +median survival time estimates and confidence interval values in the annotation table. Defaults to \code{4}.} + \item{ref_lbls}{(\code{flag})\cr whether the reference group should be explicitly printed in labels for the annotation table. If \code{FALSE} (default), only comparison groups will be printed in the table labels.} } diff --git a/man/h_tbl_median_surv.Rd b/man/h_tbl_median_surv.Rd index 06ab4c1817..fdd73e27cd 100644 --- a/man/h_tbl_median_surv.Rd +++ b/man/h_tbl_median_surv.Rd @@ -4,12 +4,15 @@ \alias{h_tbl_median_surv} \title{Helper function for survival estimations} \usage{ -h_tbl_median_surv(fit_km, armval = "All") +h_tbl_median_surv(fit_km, armval = "All", digits = 4) } \arguments{ \item{fit_km}{(\code{survfit})\cr result of \code{\link[survival:survfit]{survival::survfit()}}.} \item{armval}{(\code{string})\cr used as strata name when treatment arm variable only has one level. Default is \code{"All"}.} + +\item{digits}{(\code{integer(1)})\cr number of significant digits to use for rounding the +median survival time estimates and confidence interval values. Defaults to \code{4}.} } \value{ A summary table with statistics \code{N}, \code{Median}, and \verb{XX\% CI} (\code{XX} taken from \code{fit_km}). diff --git a/tests/testthat/test-h_km.R b/tests/testthat/test-h_km.R index b8c51aa4ef..b3c7178c5b 100644 --- a/tests/testthat/test-h_km.R +++ b/tests/testthat/test-h_km.R @@ -11,6 +11,13 @@ testthat::test_that("control_surv_med_annot works with default settings", { testthat::expect_snapshot(res) }) +testthat::test_that("control_surv_med_annot works with custom digits", { + result <- control_surv_med_annot(digits = 2) + + res <- testthat::expect_silent(result) + testthat::expect_snapshot(res) +}) + testthat::test_that("control_coxph_annot works with default settings", { result <- control_coxph_annot() @@ -84,6 +91,13 @@ testthat::test_that("h_tbl_median_surv estimates median survival time with CI", testthat::expect_snapshot(res) }) +testthat::test_that("h_tbl_median_surv respects digits parameter", { + result <- h_tbl_median_surv(fit_km = test_fit, digits = 2) + + res <- testthat::expect_silent(result) + testthat::expect_snapshot(res) +}) + testthat::test_that("h_tbl_coxph_pairwise estimates HR, CI and pvalue", { df <- tern_ex_adtte %>% filter(PARAMCD == "OS") %>% From 1bcd0eb910ad399aadfd1e7930a2cbec26b145bd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 17:31:44 +0000 Subject: [PATCH 3/5] Add examples showing how to use the digits parameter Co-authored-by: shajoezhu <3692541+shajoezhu@users.noreply.github.com> --- R/h_km.R | 2 ++ man/control_annot.Rd | 1 + man/h_tbl_median_surv.Rd | 1 + 3 files changed, 4 insertions(+) diff --git a/R/h_km.R b/R/h_km.R index 480285acfb..9f08db81f4 100644 --- a/R/h_km.R +++ b/R/h_km.R @@ -28,6 +28,7 @@ NULL #' #' @examples #' control_surv_med_annot() +#' control_surv_med_annot(digits = 2) #' #' @export control_surv_med_annot <- function(x = 0.8, y = 0.85, w = 0.32, h = 0.16, fill = TRUE, digits = 4) { @@ -136,6 +137,7 @@ h_xticks <- function(data, xticks = NULL, max_time = NULL) { #' data = adtte #' ) #' h_tbl_median_surv(fit_km = fit) +#' h_tbl_median_surv(fit_km = fit, digits = 2) #' #' @export h_tbl_median_surv <- function(fit_km, armval = "All", digits = 4) { diff --git a/man/control_annot.Rd b/man/control_annot.Rd index fd3ae5fadd..dedb9ec610 100644 --- a/man/control_annot.Rd +++ b/man/control_annot.Rd @@ -64,6 +64,7 @@ by setting it as the \code{control_annot_coxph} argument. }} \examples{ control_surv_med_annot() +control_surv_med_annot(digits = 2) control_coxph_annot() diff --git a/man/h_tbl_median_surv.Rd b/man/h_tbl_median_surv.Rd index fdd73e27cd..c38c35ba11 100644 --- a/man/h_tbl_median_surv.Rd +++ b/man/h_tbl_median_surv.Rd @@ -32,5 +32,6 @@ fit <- survfit( data = adtte ) h_tbl_median_surv(fit_km = fit) +h_tbl_median_surv(fit_km = fit, digits = 2) } From c6a28a771800b8c462f1c9a76b0ea5c5f3ec7f4f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 17:32:48 +0000 Subject: [PATCH 4/5] Address code review feedback: move validation and add fallback for digits Co-authored-by: shajoezhu <3692541+shajoezhu@users.noreply.github.com> --- R/g_km.R | 2 +- R/h_km.R | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/R/g_km.R b/R/g_km.R index e5f189ac06..df95e104b8 100644 --- a/R/g_km.R +++ b/R/g_km.R @@ -539,7 +539,7 @@ g_km <- function(df, surv_med_tbl <- h_tbl_median_surv( fit_km = fit_km, armval = armval, - digits = control_annot_surv_med[["digits"]] + digits = control_annot_surv_med[["digits"]] %||% 4 ) bg_fill <- if (isTRUE(control_annot_surv_med[["fill"]])) "#00000020" else control_annot_surv_med[["fill"]] diff --git a/R/h_km.R b/R/h_km.R index 9f08db81f4..2fa407077f 100644 --- a/R/h_km.R +++ b/R/h_km.R @@ -141,6 +141,8 @@ h_xticks <- function(data, xticks = NULL, max_time = NULL) { #' #' @export h_tbl_median_surv <- function(fit_km, armval = "All", digits = 4) { + checkmate::assert_int(digits, lower = 1) + y <- if (is.null(fit_km$strata)) { as.data.frame(t(summary(fit_km)$table), row.names = armval) } else { @@ -149,7 +151,6 @@ h_tbl_median_surv <- function(fit_km, armval = "All", digits = 4) { rownames(tbl) <- matrix(unlist(rownames_lst), ncol = 2, byrow = TRUE)[, 2] as.data.frame(tbl) } - checkmate::assert_int(digits, lower = 1) conf.int <- summary(fit_km)$conf.int # nolint y$records <- round(y$records) From 344bf6967dad21e174b3898ba405915b2a91ea6b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 17:33:40 +0000 Subject: [PATCH 5/5] Fix trailing whitespace for consistent formatting Co-authored-by: shajoezhu <3692541+shajoezhu@users.noreply.github.com> --- R/g_km.R | 4 ++-- R/h_km.R | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/R/g_km.R b/R/g_km.R index df95e104b8..326ca3f61e 100644 --- a/R/g_km.R +++ b/R/g_km.R @@ -537,8 +537,8 @@ g_km <- function(df, # add median survival time annotation table if (annot_surv_med) { surv_med_tbl <- h_tbl_median_surv( - fit_km = fit_km, - armval = armval, + fit_km = fit_km, + armval = armval, digits = control_annot_surv_med[["digits"]] %||% 4 ) bg_fill <- if (isTRUE(control_annot_surv_med[["fill"]])) "#00000020" else control_annot_surv_med[["fill"]] diff --git a/R/h_km.R b/R/h_km.R index 2fa407077f..78d56ee693 100644 --- a/R/h_km.R +++ b/R/h_km.R @@ -142,7 +142,7 @@ h_xticks <- function(data, xticks = NULL, max_time = NULL) { #' @export h_tbl_median_surv <- function(fit_km, armval = "All", digits = 4) { checkmate::assert_int(digits, lower = 1) - + y <- if (is.null(fit_km$strata)) { as.data.frame(t(summary(fit_km)$table), row.names = armval) } else { @@ -151,7 +151,7 @@ h_tbl_median_surv <- function(fit_km, armval = "All", digits = 4) { rownames(tbl) <- matrix(unlist(rownames_lst), ncol = 2, byrow = TRUE)[, 2] as.data.frame(tbl) } - + conf.int <- summary(fit_km)$conf.int # nolint y$records <- round(y$records) y$median <- signif(y$median, digits)