diff --git a/NEWS.md b/NEWS.md index c8c2415d3a..7aec962f7d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -5,6 +5,10 @@ * Added `keep_level_order` split function to retain original order of levels in a split. * Added `level_order` split function to reorder manually the levels. +### Enhancements +* Added `ref_group_coxph` parameter to `g_km` to specify the reference group used for pairwise Cox-PH calculations when `annot_coxph = TRUE`. +* Added `annot_coxph_ref_lbls` parameter to `g_km` to enable printing the reference group in table labels when `annot_coxph = TRUE`. + ### Miscellaneous * Specified minimal version of package dependencies. diff --git a/R/kaplan_meier_plot.R b/R/kaplan_meier_plot.R index 3ee070afde..a649c79683 100644 --- a/R/kaplan_meier_plot.R +++ b/R/kaplan_meier_plot.R @@ -61,6 +61,10 @@ #' * `ties` (`string`)\cr method for tie handling. Default is `"efron"`, #' can also be set to `"breslow"` or `"exact"`. See more in [survival::coxph()] #' * `conf_level` (`proportion`)\cr confidence level of the interval for HR. +#' @param ref_group_coxph (`character`)\cr level of arm variable to use as reference group in calculations for +#' `annot_coxph` table. If `NULL` (default), uses the first level of the arm variable. +#' @param annot_coxph_ref_lbls (`flag`)\cr whether the reference group should be explicitly printed in labels for the +#' `annot_coxph` table. If `FALSE` (default), only comparison groups will be printed in `annot_coxph` table labels. #' @param position_coxph (`numeric`)\cr x and y positions for plotting [survival::coxph()] model. #' @param position_surv_med (`numeric`)\cr x and y positions for plotting annotation table estimating median survival #' time per group. @@ -204,6 +208,8 @@ g_km <- function(df, annot_stats = NULL, annot_stats_vlines = FALSE, control_coxph_pw = control_coxph(), + ref_group_coxph = NULL, + annot_coxph_ref_lbls = FALSE, position_coxph = c(-0.03, -0.02), position_surv_med = c(0.95, 0.9), width_annots = list(surv_med = grid::unit(0.3, "npc"), coxph = grid::unit(0.4, "npc"))) { @@ -383,6 +389,8 @@ g_km <- function(df, df = df, variables = variables, control_coxph_pw = control_coxph_pw, + ref_group_coxph = ref_group_coxph, + annot_coxph_ref_lbls = annot_coxph_ref_lbls, x = position_coxph[1], y = position_coxph[2], width = if (!is.null(width_annots[["coxph"]])) width_annots[["coxph"]] else grid::unit(0.4, "npc"), @@ -1348,12 +1356,19 @@ h_grob_y_annot <- function(ylab, yaxis) { #' @export h_tbl_coxph_pairwise <- function(df, variables, - control_coxph_pw = control_coxph()) { + ref_group_coxph = NULL, + control_coxph_pw = control_coxph(), + annot_coxph_ref_lbls = FALSE) { assert_df_with_variables(df, variables) + checkmate::assert_choice(ref_group_coxph, levels(df[[variables$arm]]), null.ok = TRUE) + checkmate::assert_flag(annot_coxph_ref_lbls) + arm <- variables$arm df[[arm]] <- factor(df[[arm]]) - ref_group <- levels(df[[arm]])[1] - comp_group <- levels(df[[arm]])[-1] + + ref_group <- if (!is.null(ref_group_coxph)) ref_group_coxph else levels(df[[variables$arm]])[1] + comp_group <- setdiff(levels(df[[arm]]), ref_group) + results <- Map(function(comp) { res <- s_coxph_pairwise( df = df[df[[arm]] == comp, , drop = FALSE], @@ -1377,6 +1392,8 @@ h_tbl_coxph_pairwise <- function(df, row.names(res_df) <- comp res_df }, comp_group) + if (annot_coxph_ref_lbls) names(results) <- paste(comp_group, "vs.", ref_group) + do.call(rbind, results) } diff --git a/man/g_km.Rd b/man/g_km.Rd index ae63d76b86..487d755c5b 100644 --- a/man/g_km.Rd +++ b/man/g_km.Rd @@ -37,6 +37,8 @@ g_km( annot_stats = NULL, annot_stats_vlines = FALSE, control_coxph_pw = control_coxph(), + ref_group_coxph = NULL, + annot_coxph_ref_lbls = FALSE, position_coxph = c(-0.03, -0.02), position_surv_med = c(0.95, 0.9), width_annots = list(surv_med = grid::unit(0.3, "npc"), coxph = grid::unit(0.4, "npc")) @@ -143,6 +145,12 @@ can also be set to \code{"breslow"} or \code{"exact"}. See more in \code{\link[s \item \code{conf_level} (\code{proportion})\cr confidence level of the interval for HR. }} +\item{ref_group_coxph}{(\code{character})\cr level of arm variable to use as reference group in calculations for +\code{annot_coxph} table. If \code{NULL} (default), uses the first level of the arm variable.} + +\item{annot_coxph_ref_lbls}{(\code{flag})\cr whether the reference group should be explicitly printed in labels for the +\code{annot_coxph} table. If \code{FALSE} (default), only comparison groups will be printed in \code{annot_coxph} table labels.} + \item{position_coxph}{(\code{numeric})\cr x and y positions for plotting \code{\link[survival:coxph]{survival::coxph()}} model.} \item{position_surv_med}{(\code{numeric})\cr x and y positions for plotting annotation table estimating median survival diff --git a/man/h_tbl_coxph_pairwise.Rd b/man/h_tbl_coxph_pairwise.Rd index af1ddaac9f..025b397000 100644 --- a/man/h_tbl_coxph_pairwise.Rd +++ b/man/h_tbl_coxph_pairwise.Rd @@ -4,7 +4,13 @@ \alias{h_tbl_coxph_pairwise} \title{Helper Function: Pairwise \code{CoxPH} table} \usage{ -h_tbl_coxph_pairwise(df, variables, control_coxph_pw = control_coxph()) +h_tbl_coxph_pairwise( + df, + variables, + ref_group_coxph = NULL, + control_coxph_pw = control_coxph(), + annot_coxph_ref_lbls = FALSE +) } \arguments{ \item{df}{(\code{data.frame})\cr data set containing all analysis variables.} @@ -17,6 +23,9 @@ h_tbl_coxph_pairwise(df, variables, control_coxph_pw = control_coxph()) \item \code{strat} (\code{character} or \code{NULL})\cr variable names indicating stratification factors. }} +\item{ref_group_coxph}{(\code{character})\cr level of arm variable to use as reference group in calculations for +\code{annot_coxph} table. If \code{NULL} (default), uses the first level of the arm variable.} + \item{control_coxph_pw}{(\code{list})\cr parameters for comparison details, specified by using the helper function \code{\link[=control_coxph]{control_coxph()}}. Some possible parameter options are: \itemize{ @@ -26,6 +35,9 @@ Default method is \code{"log-rank"}, can also be set to \code{"wald"} or \code{" can also be set to \code{"breslow"} or \code{"exact"}. See more in \code{\link[survival:coxph]{survival::coxph()}} \item \code{conf_level} (\code{proportion})\cr confidence level of the interval for HR. }} + +\item{annot_coxph_ref_lbls}{(\code{flag})\cr whether the reference group should be explicitly printed in labels for the +\code{annot_coxph} table. If \code{FALSE} (default), only comparison groups will be printed in \code{annot_coxph} table labels.} } \value{ A \code{data.frame} containing statistics \code{HR}, \verb{XX\% CI} (\code{XX} taken from \code{control_coxph_pw}), diff --git a/tests/testthat/_snaps/g_km/g-km-ref-group-coxph.svg b/tests/testthat/_snaps/g_km/g-km-ref-group-coxph.svg new file mode 100644 index 0000000000..ea7c449acd --- /dev/null +++ b/tests/testthat/_snaps/g_km/g-km-ref-group-coxph.svg @@ -0,0 +1,657 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +N + + + + +69 + + + + +ARM A + + + + +Median + + + + +73 + + + + +ARM B + + + + +95% CI + + + + +58 + + + + +ARM C + + + + +974.6 + + + + +727.8 + + + + +632.3 + + + + +(685.2, 1501) + + + + +(555.8, 1000) + + + + +(391.3, 792.1) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +HR + + + + +0.71 + + + + +ARM A vs. ARM B + + + + +95% CI + + + + +1.22 + + + + +ARM C vs. ARM B + + + + +p-value (log-rank) + + + + +(0.48, 1.06) + + + + +(0.81, 1.85) + + + + +0.0905 + + + + +0.3371 + + +0.00 +0.25 +0.50 +0.75 +1.00 + + + + + +Survival Probability + + + + + + +0 +1000 +2000 +3000 +4000 +5000 +Days + + + + + + + +ARM A +ARM B +ARM C + + + +Censored +Patients at Risk: + + + +69 +22 +5 +1 +1 +0 +73 +21 +5 +2 +1 +0 +58 +11 +1 +0 +0 +0 + + + +ARM A +ARM B +ARM C + + + + + + +0 +1000 +2000 +3000 +4000 +5000 +Days + + diff --git a/tests/testthat/test-g_km.R b/tests/testthat/test-g_km.R index d4cde0d319..cf11cbee60 100644 --- a/tests/testthat/test-g_km.R +++ b/tests/testthat/test-g_km.R @@ -103,3 +103,22 @@ testthat::test_that("annot_at_risk_title parameter works as expected", { ) vdiffr::expect_doppelganger(title = "g_km_at_risk_title", fig = g_km_at_risk_title) }) + +testthat::test_that("ref_group_coxph parameter works as expected", { + set.seed(123) + + df <- tern_ex_adtte %>% + dplyr::filter(PARAMCD == "OS") %>% + dplyr::mutate(is_event = CNSR == 0) + + variables <- list(tte = "AVAL", is_event = "is_event", arm = "ARMCD") + + g_km_ref_group_coxph <- g_km( + df = df, + variables = variables, + annot_coxph = TRUE, + ref_group_coxph = "ARM B", + annot_coxph_ref_lbls = TRUE + ) + vdiffr::expect_doppelganger(title = "g_km_ref_group_coxph", fig = g_km_ref_group_coxph) +})