Kaplan-Meier: SE for absolute difference in time-point survival

Hi datamethods community! This is my first post here (or any statistics forum for that matter) so please bear with me.

I work as an epidemiologist in a large cancer registry and often compare Kaplan-Meier curves between groups of patients with different morphologic subgroups, tumor sizes, presence vs. absence of distant metastasis etc.

In a current project I was asked to report the absolute survival difference at 5 years of follow-up with the corresponding 95% CI as a measure of association between survival time and morphology group (comparison of 2 groups).

While computation of the point estimate is obvious, how would you estimate the SE of the between-group difference of the two 5-year Kaplan-Meier estimates? I was surprised to find very little information on this online or in survival analysis textbooks.

My naive take was to calculate the variances from the two individual (Greenwood) SEs from my survival::survfit() output at the appropriate K-M estimate by squaring them, then using the sum of the individual variances as the variance of the difference. From there I would calculate SEs and CIs as usual.

  1. Am I missing something in the calculation of the SE?
  2. Are there any strong arguments against using this difference as a measure of association? I like the intuitive interpretation of the absolute difference compared to a HR, especially as the analysis is targeted at pathologists, who are not necessarily familiar with relative effect measures. Of course, some information is thrown away by focusing on one point of the survival curve.

Thank you!

1 Like

The absolute survival difference at 5 years is the difference between the percent (proportion) of survivors in group 1 and the percent (proportion) of survivors in group 2. Then use the confidence intervals for the difference Between two proportions, e.g. 9.3 - Confidence Intervals for the Difference Between Two Population Proportions or Means | STAT 100

That is applicable if no one stops follow-up before the maximum follow-up time.

1 Like

Ok, it is correct for all who achieve 5 years cut-off point or died before it. In other times we always talk about relative risks, not absolute

No that’s not exactly what I said. 5y would have to be the minimum follow-up time, not just a cutoff. And absolute risks are important just like relative hazard.

Yes, but if the patient died before he also should be included. So we have two groups of patients: 1 - who has the 5 years follow-up time and 2 - who died before 5 years. Right?

Yes I was referring to the uncensored observations. To use the simple standard error all the censoring times have to be equal and no death can occur after this time.

1 Like

Thank you both for your replies!
I forgot to mention in my original post that the outcome is disease-specific survival, so there is censoring from competing causes of death before 5 years of follow up.
@KirovDoc: The formula in your link for the standard error of a difference of two statistics is what I tried to describe as my naive solution.

It makes sense to me, that the standard error should take censoring into account. So what would be the solution in the case of censored data?

You can use censored cases but only those who have a 5y follow-up period. E.g., there are 10 cases. 8 patients lived 5 y or more, 1 died after a 2 y follow-up, and 1 is lived in a 4y follow-up period and he is still alive. So, for absolute risk calculation, you should take only 9 cases without the last. The absolute risk of death during the 5 y period will be 1/9. The last case is censored after 4 y period and he should be excluded from the analysis. If I right understood what you need.

No. Please study survival analysis texts in detail. You create a bias by excluding observations. All observations must be included in the analysis. The point was instead that if you want to use simple proportions and their simple standard errors, censoring cannot be an issue, i.e., the shortest censoring time must equal the longest censoring time in the data and no events are observed after this censoring time.


Whatever method you use to construct CIs for the survival estimates in the two proportions - or any other quantities - it is straightforward to construct a CI for the difference between them - assuming independence of course. Use the Excel spreadsheet MOVER-D.xls freely downloadable from http://profrobertnewcomberesources.yolasite.com/

I’m not sure if this is skirting the issue but could you just use bootstrapping to estimate both the SE and the 95% CI? In this case you could stratify by whatever estimate you are interested in.

1 Like

It’s not super useful to bootstrap the wrong quantity. But in this case we already have SE estimates dedicated to Kaplan-Meier estimates and can do a decent job by taking the square root of the sum of squares of two SEs.

Oh yes - this wouldn’t be appropriate for a difference of proportions (due to the reasons you mentioned) but for a difference of a KM estimate of 5 year survival. The KM estimate wouldn’t suffer from the issues you mentioned right?

Just for my own education I calculated the CI both ways. The values are effectively the same



km <- survfit(Surv(time, status) ~ sex, data = lung)
year1_surv_est <- summary(km, t = 365)

## Method 1
year1_point_male <- year1_surv_est$surv[1]
year1_std_male <- year1_surv_est$std.err[1]

year1_point_female <- year1_surv_est$surv[2]
year1_std_female <- year1_surv_est$std.err[2]

diff_est <- year1_point_male -  year1_point_female
## > -.19 (male survival is 19% lower than females at 1 year)

diff_std <- sqrt(year1_std_male^2 + year1_std_female^2)
## > .073 (standard error around this estimate of .073)

diff_est + c(-1, 1) * 1.96 * diff_std
## > (-.335, -.046) (95% CI)

## Bootstrapping
boot_func <- function(data, index) {
  data <- data[index, ]
  km <- survfit(Surv(time, status) ~ sex, data = data)
  year1_surv_est <- summary(km, t = 365)
  year1_point_male <- year1_surv_est$surv[1]
  year1_point_female <- year1_surv_est$surv[2]
  year1_point_male - year1_point_female

boot_obj <- boot(lung, boot_func, R = 10000)
boot.ci(boot_obj, type = "perc")
## < (-0.334, -0.043) (95% CI via percentile) 
1 Like