Kaplan-Meier and the Log-Rank Test
In Lesson 1 you met Dr. Meera Rao's heart-failure trial and the two things that make time-to-event data special: right-censoring (patients still alive when we last saw them) and the survival curve \(S(t)\), the probability of surviving past time \(t\). You could describe such a curve. Now you will build one from real, censored data, read its median, and answer the question Dr. Rao actually cares about: does her new drug beat the standard one, or is the gap just noise?
By the end of this lesson you will be able to:
- Estimate a Kaplan-Meier survival curve from censored data, by hand and with
survfit, and read its median - Read a KM curve correctly: the steps, the censoring ticks, and the 50% crossing
- Compare two arms with the log-rank test, and say exactly what its p-value does and does not claim
Prerequisites: Lesson 1 (right-censoring, the Surv(time, status) outcome, the survival function \(S(t)\) and its median). You can run R and read a small table.
Why you cannot just count heads
Here is the obvious idea, and why it breaks. To estimate survival at month 15, count how many of Dr. Rao's 15 standard-arm patients are still alive and divide by 15. Simple, until you hit a censored patient.
One standard-arm patient was last seen alive at 9.4 months, then lost to follow-up: she moved, or the study ended for her, or she simply stopped coming in. At month 15, is she alive or dead? We do not know. Counting her as alive is too optimistic. Dropping her entirely throws away a real fact, that she survived at least 9.4 months. Neither choice is honest, and every censored patient forces the same bad choice.
Kaplan and Meier's fix is to never ask the month-15 question directly. Instead, walk forward in time and stop only at the moments a death actually happens. At each death, look only at the patients still under observation right then, the at-risk set, and ask a tiny question: of those at risk, what fraction survived this instant? A censored patient counts fully, right up until the moment we lose her, and then quietly leaves the at-risk set without ever being called alive-or-dead.
The product-limit estimator, by hand
Line up the standard arm's death times in order: \(t_1 < t_2 < \dots\). At each death time \(t_i\), let \(d_i\) be the number of deaths at that instant and \(n_i\) the number at risk just before it. The fraction who survive that instant is \(1 - d_i/n_i\). To survive past time \(t\), a patient must survive every death instant up to \(t\), so the survival estimate is the running product of those fractions:
\[ \hat S(t) = \prod_{t_i \le t}\left(1 - \frac{d_i}{n_i}\right). \]
That product is why it is called the product-limit estimator. Read it left to right: start at \(\hat S = 1\) (everyone alive at time 0), and each time a death happens, multiply by the fraction who survived it. Between deaths the curve is flat, because nothing changed. Walk it down the standard arm:
| time \(t\) (mo) | at risk \(n_i\) | deaths \(d_i\) | survive this instant \(1 - d_i/n_i\) | \(\hat S(t)\) |
|---|---|---|---|---|
| 0 | 15 | 0 | - | 1.000 |
| 2.7 | 15 | 1 | 14/15 | 0.933 |
| 3.2 | 14 | 1 | 13/14 | 0.867 |
| 4.5 | 13 | 1 | 12/13 | 0.800 |
| 5.5 | 12 | 1 | 11/12 | 0.733 |
| 6.1 | 11 | 1 | 10/11 | 0.667 |
| 7.0 | 10 | 1 | 9/10 | 0.600 |
| 8.3 | 9 | 1 | 8/9 | 0.533 |
| 9.4 (censored) | 8 | 0 | - (curve flat) | 0.533 |
| 11.5 | 7 | 1 | 6/7 | 0.457 |
Look carefully at the censored row. At 9.4 months a patient leaves, but nobody died, so \(\hat S\) does not drop, it stays at 0.533. What DOES change is the denominator: the at-risk set falls from 8 to 7. So when the next death arrives at 11.5 months, we multiply by \(1 - 1/7 = 6/7\) instead of by \(1 - 1/8\). The censoring made the next death "count for more."
The same curve with survfit
You never build that table by hand in practice; the survival package does it exactly. Each lesson runs in a fresh R session, so we create the standard arm inline first. The outcome goes into Surv(time, status), where status is 1 for a death and 0 for a censored patient, and survfit returns the product-limit estimate:
Those survival numbers are exactly your hand table. And notice the sleight of hand in the n.risk column: it jumps straight from 9 (at 8.3) to 7 (at 11.5). The missing patient is the one censored at 9.4, gone from the denominator with no death row of her own, precisely the mechanic you traced above. Now ask R for the median:
median = 11.5, matching the hand calculation. The NA for the upper confidence limit is honest reporting: the curve never climbs back, so R cannot pin down the upper end from this much data.
What a Kaplan-Meier curve is telling you
The widget below shows a two-arm KM plot (an illustrative trial, not Dr. Rao's exact numbers). Ignore the specific values and read the shape, because every survival paper you ever open is read the same four ways:
- The steps. Each curve is flat, then drops straight down at a death, then flat again. A KM curve only ever moves at a death; the height of each drop is that \(1 - d_i/n_i\) factor.
- The ticks. The little vertical marks are censored patients, still alive when last seen. A tick is not a death; the curve does not step down there, it just loses one from the at-risk set.
- The median. Where a curve crosses the dashed 50% line, drop straight down to the time axis: that time is the median survival for that arm.
- The gap. The vertical distance between the two curves is the survival advantage of one arm over the other, and it is exactly what the log-rank test, coming next, turns into a single number.
Toggle the arms and watch the higher curve's median sit far to the right of the lower one's.
The tick that is not a death
On Dr. Rao's standard-arm curve, at month 9.4 there is a small vertical tick, but the curve does not step down. At month 11.5 it does drop. What does the tick at 9.4 tell you?
The log-rank test: observed versus expected deaths
Now put both arms on the same plot. The new-drug curve sits higher, so its median (22.5 months, you will fit it in a moment) is well to the right of the standard arm's 11.5. But eyeballing a gap is not proof: with only 15 patients an arm, a gap that size could happen by chance. The log-rank test turns "the curves look different" into a probability.
The idea is a running tally of surprise. Walk forward through every death time in the pooled data. At each death time \(t_i\), suppose for a moment the two arms were truly identical. Then a death should land in an arm just in proportion to how many of that arm are still at risk. With \(n_i\) patients at risk in total, \(n_i^{\text{s}}\) of them in the standard arm, and \(d_i\) deaths at that instant, the expected number of standard-arm deaths if the arms were identical is
\[ e_i^{\text{s}} = d_i \times \frac{n_i^{\text{s}}}{n_i}. \]
Make it concrete at the very first death, month 2.7. All 30 patients are still at risk (the earliest new-arm event is not until month 9), so \(n_i = 30\) and \(n_i^{\text{s}} = 15\), and there is \(d_i = 1\) death. The expected standard-arm deaths are \(1 \times 15/30 = 0.5\), and the same 0.5 for the new arm. But the patient who actually died was in the standard arm, so observed = 1, expected = 0.5: a small excess of half a death for the standard arm at this one time.
Add those observed and expected counts across every death time to get the arm's totals, \(O\) and \(E\). The test statistic squares the gap between them and divides by its variance \(V\) under the "identical arms" assumption:
\[ \chi^2 = \frac{(O - E)^2}{V}. \]
One number, one degree of freedom for two arms. If the arms really are identical, \(O\) and \(E\) stay close and \(\chi^2\) is small; a large \(\chi^2\) means one arm kept dying more (or less) than chance allows.
The log-rank test with survdiff
survdiff runs that whole tally for you. We build the full 30-patient trial inline, fit both arms with one survfit, then test the gap. First the two medians:
The new arm's median (22.5 months) is nearly double the standard arm's (11.5). Now test whether that gap is real:
Read it straight across. The standard arm had 11 deaths observed but only 6.3 expected if the arms were identical: it died more than its share. The new arm mirrors it, 9 observed against 13.7 expected: it died less. Those gaps combine into Chisq = 5.4 on 1 degree of freedom, giving p = 0.02. The difference between Dr. Rao's two curves is unlikely to be chance.
Run the test yourself
trial is already in memory with months, status, and arm. Write the log-rank test that compares survival between the two arms. Fill in the variable that splits the patients into groups, to the right of the ~.
Show answer
survdiff(Surv(months, status) ~ arm, data = trial)What the log-rank test will not tell you
A p-value of 0.02 is a real result, but a narrow one. Three limits are worth carrying into every survival analysis you do, and together they are exactly why this course does not stop here.
- No effect size. The log-rank says the curves differ; it never says by how much. It cannot tell you "the new drug roughly halves the risk of dying" or "adds 11 months of median survival." A tiny, useless difference measured on 100,000 patients gives a tiny p just as easily as a big one on 30.
- It weighs the whole curve, not the medians. The test sums observed-minus-expected across all follow-up. Two arms with the same median but different shapes can differ; two with different medians but curves that cross can wash out to nothing. Read the curve, not just its midpoint.
- It is strongest when the gap is steady. The log-rank has the most power to detect a difference when one arm's hazard stays a roughly constant multiple of the other's over time (the curves do not cross). When an early advantage reverses later, the test can badly understate a real difference.
Reading p = 0.02 correctly
Dr. Rao's log-rank test returns p = 0.02. A colleague offers four readings. Which one is correct?
References
Four authoritative places to take this further:
- Kaplan and Meier (1958), Nonparametric Estimation from Incomplete Observations, JASA 53(282):457 - the original paper that defined the product-limit estimator you built here.
- The survival package vignette (Therneau, CRAN) - the canonical R reference for
survfitandsurvdiff, written by the package's author. - Bland and Altman (2004), The logrank test, BMJ 328:1073 - a short, clear derivation of the observed-versus-expected logic, from the BMJ Statistics Notes series.
- An Introduction to Statistical Learning, ch. 11 (free PDF) - a worked treatment of Kaplan-Meier and the log-rank test with the same notation used here.
Lesson 2 complete
You can now turn censored follow-up into a survival curve and a verdict. The Kaplan-Meier estimator \(\hat S(t) = \prod_{t_i \le t}(1 - d_i/n_i)\) drops only at deaths and lets censored patients leave the at-risk set cleanly, so you read the standard arm's median as 11.5 months and the new arm's as 22.5 straight off the curve. The log-rank test compared the two arms by tallying observed against expected deaths at every death time, returning \(\chi^2 = 5.4\) and p = 0.02: Dr. Rao's gap is unlikely to be chance.
But the test stopped exactly where the clinician's real questions begin. It said the arms differ, not how much, and it could not rule out that the new-arm patients were simply younger. Next, Lesson 3: the Cox proportional hazards model, which turns "the arms differ" into a single hazard ratio and lets you adjust that number for age and anything else you measured.