1.5 Survival data in software
Software 1.5.1 (A survival object). The first patients of the lung data, the coding of the status variable, the survival object, and the numbers of events and censored times:
> library(survival)
> head(lung[, c("time", "status", "age", "sex")], 5)
time status age sex
1 306 2 74 1
2 455 2 68 1
3 1010 1 56 1
4 210 2 57 1
5 883 2 60 1
> table(lung$status) # 1 = censored, 2 = died
1 2
63 165
> y <- Surv(lung$time, lung$status == 2)
> head(y, 10) # a + marks a censored time
[1] 306 455 1010+ 210 883 1022+ 310 361 218 166
> c(events = sum(lung$status == 2), censored = sum(lung$status == 1))
events censored
165 63
> round(mean(lung$status == 1), 3) # proportion censored
[1] 0.276
Reading the output. The third patient was followed for \(1010\) days and was still alive, printed \(1010+\). There are \(165\) deaths and \(63\) censored times, so \(27.6\%\) of the observations are censored: too many to ignore.
Python: with lifelines, the pair is kept as two columns, durations and event_observed.
Software 1.5.2 (The cost of ignoring censoring). Simulated exponential lifetimes with mean \(10\), each censored at an independent uniform time on \((0, 20)\); the fraction of deaths observed, and four estimates of the mean:
> set.seed(1) > n <- 10000 > t_true <- rexp(n, rate = 1/10) # true lifetimes, mean 10 > cens <- runif(n, 0, 20) # censoring time, uniform on (0, 20) > time <- pmin(t_true, cens); event <- as.numeric(t_true <= cens) > round(mean(event), 3) # fraction of deaths observed [1] 0.571 > round(c(true = mean(t_true), all_times = mean(time), + deaths_only = mean(time[event == 1]), + rate_method = sum(time) / sum(event)), 3) true all_times deaths_only rate_method 9.984 5.616 4.674 9.844
Reading the output. Only \(57\%\) of the deaths are observed. The mean of all recorded times is \(5.6\) and the mean of the uncensored times \(4.7\), both far below the true mean of about \(10\). Dividing the total time at risk by the number of deaths gives \(9.84\), close to the truth.
Python: numpy.random.exponential and numpy.minimum give the same simulation.
Software 1.5.3 (Follow-up by group). The median recorded time for the deceased and the censored patients in lung, and the numbers and proportions of deaths by sex (\(1\) = male, \(2\) = female):
> with(lung, tapply(time, status, median)) # median time by status 1 2 284 226 > with(lung, table(sex, status)) status sex 1 2 1 26 112 2 37 53 > round(prop.table(with(lung, table(sex, status)), 1), 3) status sex 1 2 1 0.188 0.812 2 0.411 0.589
Reading the output. \(81\%\) of the men died during the study but only \(59\%\) of the women. This suggests that women survived longer, but the comparison is not yet fair: the groups may have been followed for different lengths of time. The Kaplan–Meier curves and the log-rank test of later sections make such comparisons properly.
Python: pandas.crosstab(df.sex, df.status, normalize=’index’).
Questions on this section
Stuck on something here? Ask below and it stays attached to this topic.