This function takes a Kaplan-Meier model fit object (from survival::survfit) and calculate survival estimates at a specified time, and Median Survival Estimates. This can be performed on an overall KM fit or a fit including a categorical variable (strata).
pretty_km_output(fit, time_est = NULL, group_name = NULL, title_name = NULL, surv_est_prefix = "Time", surv_est_digits = 2, median_est_digits = 1, output_type = NULL)
fit | survfit object (with or without single strata variable) |
---|---|
time_est | numerical vector of time estimates. If NULL (default) no time estimates are calculated |
group_name | strata variable name. If NULL and strata exists then using variable |
title_name | title to use |
surv_est_prefix | prefix to use in survival estimate names. Default is Time (i.e. Time:5, Time:10,...) |
surv_est_digits | number of digits to round p values for survival estimates for specified times |
median_est_digits | number of digits to round p values for Median Survival Estimates |
output_type | output type, either NULL (default), "latex", or "html" (making special charaters latex friendly) |
A tibble with: Name
(if provided), Group
(if strata variable in fit), Level
(if strata variable in fit), Median Estimate
, Time:X
(Survival estimates for each time provided, if any). In no strata variable tibble is one row, otherwise nrows = number of strata levels.
Currently works with multiple strata in the fit (i.e. survfit(Surv(time, event) ~ x1 + x2)
), although level and Group
column names may be off.
# Basic linear model example set.seed(542542522) ybin <- sample(0:1, 100, replace = TRUE) ybin2 <- sample(0:1, 100, replace = TRUE) y <- rexp(100,.1) x1 <- factor(sample(LETTERS[1:2],100,replace = TRUE)) x2 <- factor(sample(letters[1:4],100,replace = TRUE)) my_fit <- survival::survfit(survival::Surv(y, ybin) ~ 1) my_fit2 <- survival::survfit(survival::Surv(y, ybin) ~ x1) my_fit3 <- survival::survfit(survival::Surv(y, ybin) ~ x2) my_fit_y2 <- survival::survfit(survival::Surv(y, ybin2) ~ 1) pretty_km_output(fit = my_fit3, time_est = c(5,10), title_name = 'Overall Fit')#> # A tibble: 4 x 8 #> Name Group Level N `N Events` `Median Estimate` `Time:5` `Time:10` #> <chr> <chr> <chr> <int> <dbl> <chr> <chr> <chr> #> 1 Overal… x2 a 25 11 9.3 (8.4, N.E.) 0.83 (0.69… 0.47 (0.27… #> 2 Overal… x2 b 20 11 24.6 (6.5, N.E.) 0.72 (0.54… 0.66 (0.48… #> 3 Overal… x2 c 22 10 14.7 (6.9, N.E.) 0.80 (0.64… 0.68 (0.49… #> 4 Overal… x2 d 33 16 10.6 (7.6, N.E.) 0.90 (0.79… 0.54 (0.37…library(dplyr) km_info <- bind_rows( pretty_km_output(fit = my_fit, time_est = c(5,10), group_name = 'Overall', title_name = 'Overall Survival---ybin'), pretty_km_output(fit = my_fit2, time_est = c(5,10), group_name = NULL, title_name = 'Overall Survival---ybin'), pretty_km_output(fit = my_fit3, time_est = c(5,10), group_name = 'x2', title_name = 'Overall Survival---ybin'), pretty_km_output(fit = my_fit_y2, time_est = c(5,10), group_name = 'Overall', title_name = 'Overall Survival---ybin2'), ) %>% select(Group, Level, everything()) kableExtra::kable(km_info, 'html', caption = 'Survival Percentage Estimates at 5 and 10 Years') %>% kableExtra::collapse_rows(1:2, row_group_label_position = 'stack', headers_to_remove = 1:2)#> <table> #> <caption>Survival Percentage Estimates at 5 and 10 Years</caption> #> <thead> #> <tr> #> <th style="text-align:left;"> Group </th> #> <th style="text-align:left;"> Level </th> #> <th style="text-align:left;"> Name </th> #> <th style="text-align:right;"> N </th> #> <th style="text-align:right;"> N Events </th> #> <th style="text-align:left;"> Median Estimate </th> #> <th style="text-align:left;"> Time:5 </th> #> <th style="text-align:left;"> Time:10 </th> #> </tr> #> </thead> #> <tbody> #> <tr> #> <td style="text-align:left;"> Overall </td> #> <td style="text-align:left;"> NA </td> #> <td style="text-align:left;"> Overall Survival---ybin </td> #> <td style="text-align:right;"> 100 </td> #> <td style="text-align:right;"> 48 </td> #> <td style="text-align:left;"> 14.7 (9.4, 25.5) </td> #> <td style="text-align:left;"> 0.82 (0.75, 0.91) </td> #> <td style="text-align:left;"> 0.59 (0.49, 0.71) </td> #> </tr> #> <tr> #> <td style="text-align:left;vertical-align: middle !important;" rowspan="2"> x1 </td> #> <td style="text-align:left;"> A </td> #> <td style="text-align:left;"> Overall Survival---ybin </td> #> <td style="text-align:right;"> 59 </td> #> <td style="text-align:right;"> 30 </td> #> <td style="text-align:left;"> 10.8 (8.4, 25.5) </td> #> <td style="text-align:left;"> 0.85 (0.76, 0.95) </td> #> <td style="text-align:left;"> 0.54 (0.41, 0.71) </td> #> </tr> #> <tr> #> #> <td style="text-align:left;"> B </td> #> <td style="text-align:left;"> Overall Survival---ybin </td> #> <td style="text-align:right;"> 41 </td> #> <td style="text-align:right;"> 18 </td> #> <td style="text-align:left;"> 17.7 (13.8, N.E.) </td> #> <td style="text-align:left;"> 0.78 (0.65, 0.93) </td> #> <td style="text-align:left;"> 0.67 (0.52, 0.85) </td> #> </tr> #> <tr> #> <td style="text-align:left;vertical-align: middle !important;" rowspan="4"> x2 </td> #> <td style="text-align:left;"> a </td> #> <td style="text-align:left;"> Overall Survival---ybin </td> #> <td style="text-align:right;"> 25 </td> #> <td style="text-align:right;"> 11 </td> #> <td style="text-align:left;"> 9.3 (8.4, N.E.) </td> #> <td style="text-align:left;"> 0.83 (0.69, 1.00) </td> #> <td style="text-align:left;"> 0.47 (0.27, 0.81) </td> #> </tr> #> <tr> #> #> <td style="text-align:left;"> b </td> #> <td style="text-align:left;"> Overall Survival---ybin </td> #> <td style="text-align:right;"> 20 </td> #> <td style="text-align:right;"> 11 </td> #> <td style="text-align:left;"> 24.6 (6.5, N.E.) </td> #> <td style="text-align:left;"> 0.72 (0.54, 0.96) </td> #> <td style="text-align:left;"> 0.66 (0.48, 0.93) </td> #> </tr> #> <tr> #> #> <td style="text-align:left;"> c </td> #> <td style="text-align:left;"> Overall Survival---ybin </td> #> <td style="text-align:right;"> 22 </td> #> <td style="text-align:right;"> 10 </td> #> <td style="text-align:left;"> 14.7 (6.9, N.E.) </td> #> <td style="text-align:left;"> 0.80 (0.64, 1.00) </td> #> <td style="text-align:left;"> 0.68 (0.49, 0.93) </td> #> </tr> #> <tr> #> #> <td style="text-align:left;"> d </td> #> <td style="text-align:left;"> Overall Survival---ybin </td> #> <td style="text-align:right;"> 33 </td> #> <td style="text-align:right;"> 16 </td> #> <td style="text-align:left;"> 10.6 (7.6, N.E.) </td> #> <td style="text-align:left;"> 0.90 (0.79, 1.00) </td> #> <td style="text-align:left;"> 0.54 (0.37, 0.79) </td> #> </tr> #> <tr> #> <td style="text-align:left;"> Overall </td> #> <td style="text-align:left;"> NA </td> #> <td style="text-align:left;"> Overall Survival---ybin2 </td> #> <td style="text-align:right;"> 100 </td> #> <td style="text-align:right;"> 59 </td> #> <td style="text-align:left;"> 13.8 (8.9, 22.4) </td> #> <td style="text-align:left;"> 0.76 (0.67, 0.85) </td> #> <td style="text-align:left;"> 0.54 (0.45, 0.67) </td> #> </tr> #> </tbody> #> </table># Real World Examples data(Bladder_Cancer) surv_obj <- survival::Surv(Bladder_Cancer$Survival_Months, Bladder_Cancer$Vital_Status == 'Dead') downstage_fit <- survival::survfit(surv_obj ~ PT0N0, data = Bladder_Cancer) pretty_km_output(fit = downstage_fit, time_est = c(24, 60), surv_est_prefix = 'Month', surv_est_digits = 3)#> # A tibble: 2 x 7 #> Group Level N `N Events` `Median Estimate` `Month:24` `Month:60` #> <chr> <chr> <int> <dbl> <chr> <chr> <chr> #> 1 PT0N0 No Comple… 131 57 48.7 (30.3, N.E.) 0.617 (0.533… 0.493 (0.39… #> 2 PT0N0 Complete … 35 2 N.E. 0.940 (0.863… 0.940 (0.86…