diff --git a/DESCRIPTION b/DESCRIPTION index 1cb7570..b4fa794 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: ggsurvfit Title: Flexible Time-to-Event Figures -Version: 1.0.0.9000 +Version: 1.0.0.9001 Authors@R: c( person("Daniel D.", "Sjoberg", , "danield.sjoberg@gmail.com", role = c("aut", "cre", "cph"), comment = c(ORCID = "0000-0003-0862-2018")), diff --git a/NEWS.md b/NEWS.md index d154107..92a0c83 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,6 +2,10 @@ * Updated legend position syntax to account for changes in ggplot v3.5.0. +* The `tidy_survfit()` (and subsequently `ggsurvfit()`) now honor the `survfit(start.time)` if specified. (#192) + +* We now allow for negative follow-up times in `tidy_survfit()` (and subsequently `ggsurvfit()`). When negative follow-up times are present users should specify `survfit(start.time)` and we print a note to this effect when not set. (#192) + # ggsurvfit 1.0.0 * By default, a model plot created with `ggsurvfit()` or `ggcuminc()` uses the color aesthetic to plot curves by the stratifying variable(s), and further, `ggcuminc()` uses the linetype aesthetic for plots that contain multiple outcomes (i.e. competing events). We now introduce the global option `"ggsurvfit.switch-color-linetype"` to switch these defaults, giving users more flexibility over the output figures. Furthermore, when the `linetype_aes=` argument is called in a situation when it does not apply, it will be silently ignored (previously, an error message _may_ have been thrown). (#166) diff --git a/R/tidy_survfit.R b/R/tidy_survfit.R index 0cf0f97..2cbd6e0 100644 --- a/R/tidy_survfit.R +++ b/R/tidy_survfit.R @@ -39,13 +39,17 @@ tidy_survfit <- function(x, } if (inherits(x, "survfitms")) type <- "cuminc" else if (is.character(type)) type <- match.arg(type) - if (!is.null(times) && any(times < 0)) { - cli_abort("The {.var times} cannot be negative.") - } + # create base tidy tibble ---------------------------------------------------- + if (is.null(x$start.time) && min(x$time) < 0) { + cli::cli_inform(c( + "!" ="Setting start time to {.val {min(x$time)}}.", + "i" = "Specify {.code ggsurvfit::survfit2(start.time)} to override this default." + )) + } df_tidy <- - survival::survfit0(x, start.time = 0) %>% + survival::survfit0(x) %>% broom::tidy() # if a competing risks model, filter on the outcome of interest diff --git a/tests/testthat/_snaps/add_risktable/sf-negative-time.svg b/tests/testthat/_snaps/add_risktable/sf-negative-time.svg new file mode 100644 index 0000000..b679b73 --- /dev/null +++ b/tests/testthat/_snaps/add_risktable/sf-negative-time.svg @@ -0,0 +1,351 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +0.25 +0.50 +0.75 +1.00 + + + + + + + + + + + + + + + + +-10 +0 +10 +20 + + + + + + + + +Time + + + + +Survival Probability + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +sf-negative_time + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +228 +0 +87 +103 +24 +148 +3 +165 + + + + + + + + + + + + + + + + + + + + + + + + +Events +At Risk + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/testthat/_snaps/add_risktable/sf-start-time.svg b/tests/testthat/_snaps/add_risktable/sf-start-time.svg new file mode 100644 index 0000000..9168eb6 --- /dev/null +++ b/tests/testthat/_snaps/add_risktable/sf-start-time.svg @@ -0,0 +1,515 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +0.25 +0.50 +0.75 +1.00 + + + + + + + + + + + + + + + + + +10 +15 +20 +25 +30 + + + + + + + + +Time + + + + +Survival Probability + + + + + + + + + + + + + + + + + + + + + +sex=Male +sex=Female + + + + + + + + + + + + + + + + +sf-start_time + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +44 +0 +24 +17 +13 +27 +7 +33 +2 +36 + + + + + + + + + + + + + + + + + + + + + + + + +Events +At Risk + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +sex=Male + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +43 +0 +22 +14 +11 +18 +3 +25 +1 +26 + + + + + + + + + + + + + + + + + + + + + + + + +Events +At Risk + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +sex=Female + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/testthat/_snaps/tidy_survfit.md b/tests/testthat/_snaps/tidy_survfit.md new file mode 100644 index 0000000..50c4970 --- /dev/null +++ b/tests/testthat/_snaps/tidy_survfit.md @@ -0,0 +1,8 @@ +# tidy_survfit() messaging + + Code + survfit(Surv(time - 500, status) ~ 1, df_lung) %>% tidy_survfit() %>% invisible() + Message + ! Setting start time to -499.835728952772. + i Specify `ggsurvfit::survfit2(start.time)` to override this default. + diff --git a/tests/testthat/test-add_risktable.R b/tests/testthat/test-add_risktable.R index 15e5ba2..149a74a 100644 --- a/tests/testthat/test-add_risktable.R +++ b/tests/testthat/test-add_risktable.R @@ -298,3 +298,25 @@ test_that("add_risktable() works with Cox models", { ) }) +test_that("add_risktable() works with ggsurvfit() `start.time` and negative times", { + expect_error( + sf_negative_time <- + survfit(Surv(time - 10, status) ~ 1, df_lung, start.time = -10) %>% + ggsurvfit() + + add_risktable(), + NA + ) + + expect_error( + sf_start_time <- + survfit(Surv(time, status) ~ sex, df_lung, start.time = 10) %>% + ggsurvfit() + + add_risktable(), + NA + ) + + skip_on_ci() + vdiffr::expect_doppelganger("sf-negative_time", sf_negative_time) + vdiffr::expect_doppelganger("sf-start_time", sf_start_time) +}) + diff --git a/tests/testthat/test-tidy_survfit.R b/tests/testthat/test-tidy_survfit.R index 3bef440..b46e804 100644 --- a/tests/testthat/test-tidy_survfit.R +++ b/tests/testthat/test-tidy_survfit.R @@ -84,7 +84,6 @@ test_that("tidy_survfit() works with survfit2()", { test_that("tidy_survfit() throws appropriate errors", { expect_error(tidy_survfit(mtcars)) - expect_error(tidy_survfit(sf1, times = -5:5)) expect_error(tidy_survfit(sf1, type = "not_a_type")) }) @@ -207,4 +206,17 @@ test_that("tidy_survfit() works with multi-state models", { ) }) - +test_that("tidy_survfit() messaging", { + # expect a message about the start time when there are negative times and no start.time + expect_snapshot( + survfit(Surv(time - 500, status) ~ 1, df_lung) %>% + tidy_survfit() %>% + invisible() + ) + # no longer see message when start.time specified + expect_invisible( + survfit(Surv(time - 500, status) ~ 1, df_lung, start.time = -500) %>% + tidy_survfit() %>% + invisible() + ) +})