The fundamental difference
Kaplan-Meier and Cox regression are both methods for analyzing time-to-event data. They are not competitors, they address different research questions and should usually appear together in a clinical trial analysis.
- Kaplan-Meier estimates the survival function: the probability of not having experienced the event by time t. It is nonparametric and makes no distributional assumptions. It describes the data.
- Cox regression estimates the hazard ratio: how much more (or less) likely subjects in one group are to experience the event at any given time, adjusting for covariates. It explains the data.
The typical analysis plan for a time-to-event endpoint: Kaplan-Meier curves for visualization and median survival estimates, log-rank test for the primary hypothesis test, Cox model for the primary effect estimate with covariate adjustment.
Kaplan-Meier: what it shows and when to use it
library(survival)
library(survminer)
# Fit Kaplan-Meier by treatment group
km_fit <- survfit(
Surv(time_to_event, event_occurred) ~ treatment,
data = trial_data
)
# KM plot with confidence intervals and risk table
ggsurvplot(km_fit,
data = trial_data,
risk.table = TRUE,
conf.int = TRUE,
pval = TRUE, # log-rank p-value on plot
pval.method = TRUE,
palette = c("#1a6fd4", "#e05c2a"),
xlab = "Time (months)",
ylab = "Progression-free survival",
break.time.by = 3
)
The KM curve is your primary visualization. The risk table below the plot is essential: it shows how many subjects are still at risk at each time point, which contextualizes the reliability of the curves (wide CIs at late time points mean few subjects remain).
The log-rank test: what it does and does not test
The log-rank test tests whether the survival functions of two or more groups are identical over the entire follow-up period. It is the standard primary hypothesis test for survival endpoints in clinical trials.
What it does not do: it does not estimate the magnitude of the treatment effect, and it has low power when the survival curves cross (which suggests a time-varying treatment effect). If your curves cross, the log-rank test p-value is misleading.
survdiff(Surv(time_to_event, event_occurred) ~ treatment, data = trial_data)
# Output: chi-squared statistic, degrees of freedom, p-value
Cox proportional hazards: the key assumptions
The Cox model assumes proportional hazards: the hazard ratio between groups is constant over time. Mathematically, the survival curves on a log(-log) scale should be parallel. Practically, this means the treatment effect does not diminish or increase over time.
cox_fit <- coxph(
Surv(time_to_event, event_occurred) ~ treatment + age + stage,
data = trial_data
)
summary(cox_fit)
# exp(coef) gives hazard ratios
# exp(confint(cox_fit)) gives 95% CI for hazard ratios
Testing the proportional hazards assumption
Always test the proportional hazards assumption. The Schoenfeld residuals test is the standard approach:
ph_test <- cox.zph(cox_fit)
print(ph_test)
# p < 0.05 for a covariate indicates violation for that term
# p < 0.05 for GLOBAL indicates overall model violation
ggcoxzph(ph_test) # visual: residuals should show no trend over time
If the global test is significant, you have a problem. The reported hazard ratio is an average over time, which may not represent the true treatment effect at any specific time point.
When Cox fails: alternatives
When proportional hazards assumption fails, you have several options:
- Time-varying covariates: allow the treatment effect to change over time by interacting treatment with time in the Cox model.
- Restricted mean survival time (RMST): estimates the difference in mean survival time up to a prespecified time horizon. No proportionality assumption required. Now recommended by many regulators as a complement to the hazard ratio.
- Flexible parametric models: fit a smooth hazard function that can accommodate non-proportional hazards.
library(survRM2)
# RMST at 24 months
rmst2(time = trial_data$time_to_event,
status = trial_data$event_occurred,
arm = ifelse(trial_data$treatment == "Active", 1, 0),
tau = 24)
# Output: RMST difference, 95% CI, p-value
Reporting survival results correctly
For a regulatory submission or a peer-reviewed publication, the survival results section should contain:
- Kaplan-Meier plot with risk table and 95% CI bands
- Median survival times with 95% CI per group
- Log-rank test p-value (with Bonferroni or hierarchical correction if multiple endpoints)
- Hazard ratio with 95% CI from Cox model (adjusted for stratification factors)
- Proportional hazards assumption test result
- RMST analysis if PH assumption is violated or as a sensitivity analysis
A practical decision guide
| Question | Method |
|---|---|
| What does survival look like over time? | Kaplan-Meier curve |
| Is the treatment effect significant? | Log-rank test |
| What is the hazard ratio, adjusted for covariates? | Cox regression |
| Do KM curves cross? | Check PH assumption, consider RMST |
| What is the mean survival benefit up to month 24? | RMST |
Key takeaway
Use Kaplan-Meier to show the data, log-rank to test significance, Cox to estimate effect size. Always test the proportional hazards assumption and report what you found. If it fails, supplement with RMST. These are not interchangeable tools: they answer different questions.