openpharma / visR

A package to wrap functionality for plots, tables and diagrams adhering to graphical principles.
https://openpharma.github.io/visR/
Other
179 stars 31 forks source link

Issue with `n.event`/`n.censor` calculation in `get_risktable.survfit()` #296

Closed ddsjoberg closed 2 years ago

ddsjoberg commented 2 years ago

I think the calculation of the number of censored patients in get_risktable.survfit() is not correct.

In the example below, we're focusing on the number of patients at risk, number of events, and the number of censored patients. In this setting the calculation of the number of events and the number of censored patients should be the same method.

In the example below, summary.survfit() indicates there are 57 events in the first 20 days (this includes day 20) and the tabulation at the bottom of the reprex confirms this is correct. However, get_risktable() shows 57 CENSORED (not events) in the first 20 days.

Also, assuming the censored and event labels are switched in get_risktable(), the number censored is not correct. The cross table at the bottom of the reprex shows that 19 patients were censored in the first 20 days (get_risktable() shows 16).

library(visR)
packageVersion("visR")
#> [1] '0.2.0'

survfit <-
  adtte %>%
  estimate_KM()

survfit %>% get_risktable(times = c(0, 20), statlist = c("n.risk", "n.event", "n.censor"))
#>   time y_values Overall
#> 1    0  At risk     254
#> 2    0 Censored       0
#> 3    0   Events       0
#> 4   20  At risk     181
#> 5   20 Censored      57
#> 6   20   Events      16
survfit %>% summary(times = c(0, 20))
#> Call: survival::survfit(formula = survival::Surv(AVAL, 1 - CNSR) ~ 
#>     1, data = adtte)
#> 
#>                 Overall 
#>  time n.risk n.event survival std.err lower 95% CI upper 95% CI
#>     0    254       0    1.000  0.0000        1.000        1.000
#>    20    181      57    0.767  0.0271        0.716        0.822

df <-
  adtte %>%
  dplyr::select(AVAL, CNSR) %>%
  dplyr::filter(AVAL <= 20) %>%
  dplyr::arrange(dplyr::desc(AVAL)) 

# tabulating CNSR and EVENT occuring in the first 20 days
with(df, table(CNSR))
#> CNSR
#>  0  1 
#> 57 19

Created on 2022-01-07 by the reprex package (v2.0.1)

SHAESEN2 commented 2 years ago

@ddsjoberg It is a bug in the labelling of the function. I will fix it.

ddsjoberg commented 2 years ago

Thank you @SHAESEN2 for taking a look!

There are two issues.

  1. The labels the number of events and the number of censored obs is flipped.
  2. The calculation of the number of censored observations is not correct. The accumulating number of events and the accumulating number of censored patients should be calculated the same way. Estimating a KM curve with the outcome as the CNSR gives us a way to check our results.
library(visR)
library(survival)
packageVersion("visR")
#> [1] '0.2.0'

# number of events to report at Day 20
survfit(Surv(AVAL, 1 - CNSR) ~ 1, adtte) |>
  summary(times = 20) |>
  purrr::pluck("n.event")
#> [1] 57

# number of censored obs to report at Day 20
survfit(Surv(AVAL, CNSR) ~ 1, adtte) |>
  summary(times = 20) |>
  purrr::pluck("n.event")
#> [1] 19

Created on 2022-01-09 by the reprex package (v2.0.1)