Kaplan-Meier Estimator — Survival Function
Statistics
Non-Parametric Estimation of Survival Probabilities
The Kaplan-Meier estimator constructs the survival function step-by-step at each event time, handling censored observations correctly. It produces the iconic survival curve used throughout medical and reliability research.
-
Clinical Trials — Estimate patient survival probabilities with varying follow-up times
-
Manufacturing — Predict component reliability with incomplete failure data
-
Customer Analytics — Model subscription duration with right-censored observations
Each step down in the survival curve represents real events, properly weighted for those still at risk.
The Kaplan-Meier estimator is a non-parametric method for estimating the survival function from time-to-event data, even when observations are censored.
Censoring
| Type | Description |
|------|------------|
| Right-censored | Event not observed before study ends |
| Left-censored | Event occurred before study began |
| Interval-censored | Event known to occur in an interval |
Kaplan-Meier Formula
The estimator is a step function that drops at each event time.
Standard Error
The 95% confidence interval is:
Log-Rank Test
The log-rank test compares survival curves between two or more groups.
| Hypothesis | Meaning |
|-----------|---------|
| : | No difference in survival between groups |
| : | Survival curves differ |
Median Survival Time
The median survival is the smallest time at which .
Python Implementation
import numpy as np
import pandas as pd
from lifelines import KaplanMeierFitter
from lifelines.statistics import logrank_test
import matplotlib.pyplot as plt
np.random.seed(42)
# Simulate survival data
n = 200
treatment = np.random.binomial(1, 0.5, n)
time = np.where(treatment,
np.random.exponential(12, n), # Treatment: longer survival
np.random.exponential(8, n)) # Control: shorter survival
censored = np.random.binomial(1, 0.2, n) # 20% censoring
event = 1 - censored
# Kaplan-Meier curves
kmf_treat = KaplanMeierFitter()
kmf_control = KaplanMeierFitter()
mask_treat = treatment == 1
kmf_treat.fit(time[mask_treat], event[mask_treat], label='Treatment')
kmf_control.fit(time[~mask_treat], event[~mask_treat], label='Control')
# Plot
fig, ax = plt.subplots(figsize=(8, 5))
kmf_treat.plot_survival_function(ax=ax)
kmf_control.plot_survival_function(ax=ax)
ax.set_title('Kaplan-Meier Survival Curves')
ax.set_xlabel('Time')
ax.set_ylabel('Survival Probability')
plt.show()
# Median survival
print(f"Treatment median: {kmf_treat.median_survival_time_:.1f}")
print(f"Control median: {kmf_control.median_survival_time_:.1f}")
# Log-rank test
result = logrank_test(time[mask_treat], time[~mask_treat],
event_observed_A=event[mask_treat],
event_observed_B=event[~mask_treat])
print(f"\nLog-rank test: ?²={result.test_statistic:.2f}, p={result.p_value:.4f}")
Worked Example
Key Takeaways
Related Topics
-
See Cox Proportional Hazards for regression with covariates
-
See Hypothesis Testing for the log-rank test framework
-
See Missing Data for handling different censoring mechanisms