pharmaverse / ggsurvfit

http://www.danieldsjoberg.com/ggsurvfit/
Other
67 stars 19 forks source link

Feature request: Extend confidence bands added via add_confidence_interval() #214

Closed slobaugh closed 3 weeks ago

slobaugh commented 1 month ago

Is your feature request related to a problem? Please describe. Confidence bands drawn in a KM curve created using ggsurvfit + add_confidence_interval() do not necessarily appear for all of the times for which they are defined.

Describe the solution you'd like Sometimes I provide KM survival estimates for a time-point past where the confidence bands end in the associated plot created using ggsurvfit, and it would be nice if the plot aligned with the estimates for consistency. I would like for the confidence bands to appear in the plot for all of the times for which they are defined.

Describe alternatives you've considered I could use survminer::ggsurvplot instead. My reprex below compares the behavior of this function with ggsurvfit.

Additional context Reprex:

# load packages
library(reprex)
library(tidyverse)
library(ggsurvfit)
library(survival)
library(survminer)
#> Loading required package: ggpubr
#> 
#> Attaching package: 'survminer'
#> The following object is masked from 'package:survival':
#> 
#>     myeloma
library(gridExtra)
#> 
#> Attaching package: 'gridExtra'
#> The following object is masked from 'package:dplyr':
#> 
#>     combine

# create data
df_fake <- tibble::tribble(
                ~time, ~status,
     24.0164271047228,       1,
     24.1478439425051,       1,
     24.3121149897331,       0,
     25.1334702258727,       1,
     25.9876796714579,       1,
     26.4804928131417,       0,
     26.7433264887064,       1,
     26.9733059548255,       0,
     27.5975359342916,       0,
     29.0102669404517,       1,
     31.7043121149897,       0,
     33.1827515400411,       0,
     33.5770020533881,       0,
     58.5770020533881,       0,
     63.5770020533881,       1
     ) 

# create kaplan-meier curve using ggsurvfit
# expect this code to create a plot with a CI band that extends up until just before the final event
# reality: the CI band ends at the final censored patient.
x <- survfit2(Surv(time, status) ~ 1, data = df_fake) %>%
  ggsurvfit() +
  add_confidence_interval() +
  add_censor_mark() +
  labs(title = "ggsurvfit plot: \n CI band ends way before \n final event") +
  coord_cartesian(xlim = c(0, 72))

# create kaplan-meier curve using survminer::ggsurvplot
# expect this code to create a plot with a CI band that extends up until just before the final event
# reality: matches expectation
km <- survfit2(Surv(time, status) ~ 1, data = df_fake)
y <- ggsurvplot(
  fit = km,
  data = df_fake, 
  conf.int = TRUE,
  palette = "black",
  xlim = c(0, 72),
  break.x.by = 20,
  legend = "none",
  title = "survminer::ggsurvplot plot: \n CI band goes up to just \n before final event"
)

# display side-by-side to visualize the difference in CI bands between the two plots
gridExtra::grid.arrange(ggsurvfit_build(x),
                        y$plot,
                        ncol = 2)

Created on 2024-05-21 with reprex v2.1.0

ddsjoberg commented 1 month ago

Hi @slobaugh !

I think we spoke about this request a few years ago, right? The ggsurvfit behavior mimics what is done in the survival package. That said, I am not against the change. But I probably wouldn't put the effort into making the change myself.

I think this would require an update to the geom_step_ribbon() function. Let me know if it's something you'd like to pursue.

library(ggsurvfit)
#> Loading required package: ggplot2

# create data
df_fake <- tibble::tribble(
  ~time, ~status,
  24.0164271047228,       1,
  24.1478439425051,       1,
  24.3121149897331,       0,
  25.1334702258727,       1,
  25.9876796714579,       1,
  26.4804928131417,       0,
  26.7433264887064,       1,
  26.9733059548255,       0,
  27.5975359342916,       0,
  29.0102669404517,       1,
  31.7043121149897,       0,
  33.1827515400411,       0,
  33.5770020533881,       0,
  58.5770020533881,       0,
  63.5770020533881,       1
) 

survfit(Surv(time, status) ~ 1, data = df_fake) |> plot()

Created on 2024-05-21 with reprex v2.1.0

ddsjoberg commented 1 month ago

@slobaugh is this something you're interested in doing? 💯

slobaugh commented 3 weeks ago

Thanks @ddsjoberg, we did chat briefly about this in person a while ago! I feel similarly to you in that I probably won't put the effort into making the change myself 🙈 but I really appreciate the reminder that ggsurvfit is mimicking the survival package's behavior here. Please close this issue as you see fit!

ddsjoberg commented 3 weeks ago

OK, I'll go ahead and close. If you ever feel the motivation to make it happen, re-open the issue :)