Kaplan-Meier Analysis
The engine for Survival Discovery. Kaplan-Meier audits the step-by-step trajectory of time-to-event data, reveal the 'Survival Probability' while mathematically neutralizing the bias of censored participants.
What is it?
Kaplan-Meier Analysisis designed to calculate survival probability over time, adjusting for right censoring so drop-outs don't bias the estimation.
The engine for Survival Discovery. Kaplan-Meier audits the step-by-step trajectory of time-to-event data, reveal the 'Survival Probability' while mathematically neutralizing the bias of censored participants.
Goals & Indications
- Survival Trajectory Audit: Construct a high-fidelity 'Step-Curve' that maps the probability of remaining event-free over time.
- Censoring Forensics: Account for participants who 'drop out' or 'finish the study' without experiencing the event.
- Group Divergence Discovery: Quantify the 'Survival Gap' between treatment and control using the definitive Log-Rank strike.
Core Idea Diagram
Claims tested
How it works
- Sort all observed times (events and censorings) in chronological order.
- For each time point, count the number of subjects at risk immediately before.
- Calculate the conditional survival rate as 1 minus events divided by at risk.
- Multiply conditional rates cumulatively to yield the step-wise survival curve.
Assumptions
Important Note
For log-rank test comparing groups. Single-group Kaplan-Meier is descriptive only (no hypothesis test). Can be one-tailed if directional survival hypothesis is pre-specified.
Worked Example
| Time (t) | At Risk (n) | Events (d) | Censored (c) | S(t) |
|---|---|---|---|---|
| 0 | 10 | 0 | 0 | 1.000 |
| 5 | 10 | 1 | 0 | 0.900 |
| 8 | 9 | 0 | 1 | 0.900 |
| 12 | 8 | 2 | 0 | 0.675 |
Kaplan-Meier Step Survival & Log-Rank Laboratory
Simulate right-censored time-to-event outcomes. Adjust hazard rates (λ) and censoring rate (γ) to observe step curves, censoring tick marks, and the Log-Rank test statistic in real-time.
| Group | Observed (O) | Expected (E) |
|---|---|---|
| Treatment (Group 1) | 16 | 24.26 |
| Control (Group 2) | 27 | 15.74 |
| Group | t = 0 | t = 4 | t = 8 | t = 12 | t = 16 | t = 20 |
|---|---|---|---|---|---|---|
| Treatment (G1) | 40 | 14 | 4 | 2 | 2 | 0 |
| Control (G2) | 40 | 11 | 0 | 0 | 0 | 0 |
The Log-Rank test identifies a statistically significant difference in survival trajectories (p = 0.0090). Treatment group (G1) hazard rate is lower than Control (G2), which translates to a prolonged survival benefit.
Hypotheses
Pragmatic null and alternative hypotheses defined in mathematical notation.
H₀: S₁(t) = S₂(t) for all time points t (survival functions are identical across groups)
Hₐ: S₁(t) ≠ S₂(t) for at least one time point (survival functions differ between groups)
For log-rank test comparing groups. Single-group Kaplan-Meier is descriptive only (no hypothesis test). Can be one-tailed if directional survival hypothesis is pre-specified.
Assumptions
The core mathematical criteria needed to ensure that statistical testing remains unbiased and valid.
Diagnostics
Checking residual plots and indices to examine model deviations and ensure standard error integrity.
- Log-Rank test for global statistical divergence between survival curves.
- Proportionality check: Visual audit of KM curves to ensure they do not cross significantly.
- Number at Risk table audit across all major timepoint milestones.
- Median Survival Time calculation with high-precision 95% Confidence Intervals.
- Censoring distribution check to identify patterns in participant dropouts.
- Hall-Wellner 95% confidence bands to visualize the global trajectory stability.
- Cumulative Hazard function audit to identify periods of peak risk.
- Restricted Mean Survival Time (RMST) comparison for a non-parametric summary.
- Sensitivity analysis for 'Informative Censoring' (checking dropout drivers).
- Comparison with Cox Proportional Hazards if baseline covariate adjustments are required.
Applied Minds
Review concrete study examples, data layout guidelines, and copy executable syntax scripts.
Lung Cancer Treatment Survival (Standard vs Experimental Chemotherapy)
Research question: Does experimental chemotherapy improve survival compared to standard treatment in advanced non-small cell lung cancer? Design: Randomized controlled trial (Standard n=68, Experimental n=65, 24-month follow-up). Outcome: Overall survival (time from randomization to death from any cause). Event rate: 72% (96/133 deaths), 28% censored at study end or lost to follow-up.
# Kaplan-Meier Survival Analysis with Log-Rank Test
# Lung cancer RCT: Standard vs Experimental chemotherapy
# Install/load required packages
library(survival) # For survfit, Surv, survdiff
library(survminer) # For ggsurvplot (enhanced visualization)
library(dplyr) # Data manipulation
# Simulate realistic lung cancer survival data (or load: data <- read.csv("lung_trial.csv"))
set.seed(2025)
# Standard treatment: median survival 11.5 months
standard_times <- rweibull(68, shape=1.2, scale=13)
standard_event <- rbinom(68, 1, 0.75) # 75% event rate
# Experimental: median survival 16.2 months (HR~0.70)
experimental_times <- rweibull(65, shape=1.2, scale=18.5)
experimental_event <- rbinom(65, 1, 0.68) # 68% event rate
# Administrative censoring at 24 months
standard_times <- pmin(standard_times, 24)
experimental_times <- pmin(experimental_times, 24)
# If time reaches 24 and subject alive, mark as censored
standard_event[standard_times >= 24] <- 0
experimental_event[experimental_times >= 24] <- 0
data <- data.frame(
treatment = c(rep("Standard", 68), rep("Experimental", 65)),
time = c(standard_times, experimental_times),
status = c(standard_event, experimental_event)
)
# === STEP 1: Data Summary ===
cat("=== Dataset Summary ===\n")
cat("Total patients:", nrow(data), "\n")
cat("Events(deaths):", sum(data$status), "\n")
cat("Censored:", sum(1 - data$status), "\n")
cat("Censoring rate:", round(mean(1 - data$status)*100, 1), "%\n\n")
data %>%
group_by(treatment) %>%
summarise(
n = n(),
events = sum(status),
censored = sum(1 - status),
median_followup = median(time)
)
# === STEP 2: Kaplan-Meier Survival Curves ===
# Create survival object: Surv(time, status)
# status = 1 for event (death), 0 for censored
surv_object <- Surv(time = data$time, event = data$status)
# Fit Kaplan-Meier curves by treatment group
km_fit <- survfit(surv_object ~ treatment, data = data)
# Print survival summary
print(km_fit)
# Output shows: n, events, median survival, 95% CI
# Detailed summary at specific timepoints
summary(km_fit, times = c(6, 12, 18, 24))
# Extract median survival times with 95% CI
cat("\n=== Median Survival Times ===\n")
print(surv_median(km_fit))
# === STEP 3: Log-Rank Test (Compare Survival Curves) ===
logrank_test <- survdiff(Surv(time, status) ~ treatment, data = data)
print(logrank_test)
# Extract chi-square and p-value
chi_sq <- logrank_test$chisq
p_value <- 1 - pchisq(chi_sq, df = 1)
cat("\n=== Log-Rank Test Results ===\n")
cat("Chi-square:", round(chi_sq, 2), "\n")
cat("df: 1\n")
cat("p-value:", round(p_value, 4), "\n")
if (p_value < 0.001) {
cat("Interpretation: Highly significant difference in survival(p < .001)\n")
} else if (p_value < 0.05) {
cat("Interpretation: Significant difference in survival(p < .05)\n")
} else {
cat("Interpretation: No significant difference in survival(p ≥ .05)\n")
}
# === STEP 4: Hazard Ratio (from Cox model for effect size) ===
library(survival)
cox_model <- coxph(Surv(time, status) ~ treatment, data = data)
summary(cox_model)
HR <- exp(coef(cox_model))
HR_CI <- exp(confint(cox_model))
cat("\n=== Hazard Ratio(Experimental vs Standard) ===\n")
cat("HR:", round(HR, 2), "\n")
cat("95% CI: [", round(HR_CI[1], 2), ",", round(HR_CI[2], 2), "]\n")
cat("Interpretation: Experimental treatment reduces hazard of death by",
round((1-HR)*100, 0), "%\n")
# === STEP 5: Visualize Survival Curves ===
# Method 1: Base R plot
plot(km_fit,
col = c("red", "blue"),
lwd = 2,
xlab = "Time(months)",
ylab = "Overall Survival Probability",
main = "Kaplan-Meier Survival Curves\nLung Cancer RCT: Standard vs Experimental Chemotherapy")
legend("topright",
legend = c("Standard", "Experimental"),
col = c("red", "blue"),
lwd = 2)
# Add median survival lines
abline(h = 0.5, lty = 2, col = "gray")
# Method 2: Enhanced plot with survminer (publication-ready)
library(survminer)
ggsurvplot(
km_fit,
data = data,
pval = TRUE, # Show log-rank p-value
pval.method = TRUE, # Show test name
conf.int = TRUE, # Show 95% CI bands
risk.table = TRUE, # Add risk table below plot
risk.table.height = 0.25,
ggtheme = theme_bw(),
palette = c("#E7B800", "#2E9FDF"),
xlab = "Time(months)",
ylab = "Overall Survival Probability",
title = "Kaplan-Meier Survival Analysis: Lung Cancer RCT",
legend.title = "Treatment",
legend.labs = c("Experimental", "Standard"),
break.time.by = 6, # X-axis breaks every 6 months
surv.median.line = "hv", # Add median survival lines
tables.theme = theme_cleantable()
)
# === STEP 6: Additional Diagnostics ===
# Check proportional hazards assumption
test_ph <- cox.zph(cox_model)
print(test_ph)
# If p > .05: proportional hazards OK (log-rank test appropriate)
# If p < .05: consider reporting curves cross or using RMST
plot(test_ph)
# Cumulative hazard plot
ggsurvplot(
km_fit,
data = data,
fun = "cumhaz",
conf.int = TRUE,
palette = c("#E7B800", "#2E9FDF"),
xlab = "Time(months)",
ylab = "Cumulative Hazard",
title = "Cumulative Hazard Function"
)
# === APA-Style Reporting ===
cat("\n=== APA-Style Report ===\n")
cat("A Kaplan-Meier survival analysis was conducted to compare overall survival\n")
cat("between standard and experimental chemotherapy in advanced NSCLC(N=133).\n")
cat("Median follow-up was 16.3 months. The experimental group(n=65) had significantly\n")
cat("longer median survival(16.2 months, 95% CI [13.8, 19.1]) compared to the\n")
cat("standard group(n=68, median 11.5 months, 95% CI [9.7, 13.6]). Log-rank test\n")
cat("showed significant difference in survival distributions(χ²(1) = 8.45, p = .004).\n")
cat("Hazard ratio indicated 30% reduction in death risk with experimental treatment\n")
cat("(HR = 0.70, 95% CI [0.48, 0.96]). At 12 months, survival probability was 62%\n")
cat("in experimental vs 48% in standard group. At 24 months: 35% vs 22%.\n")Log-rank χ²(1) = 8.45, p = .004. Experimental treatment significantly improved survival compared to standard chemotherapy. Median survival: 16.2 vs 11.5 months (4.7-month benefit). Hazard ratio 0.70 (95% CI [0.48, 0.96]) indicates 30% reduction in death risk. At 12 months, 62% vs 48% survival; at 24 months, 35% vs 22%. Clinically meaningful and statistically significant survival advantage for experimental treatment.
Alternatives
Structured fallback pathways for choosing alternative tests when normality or slopes requirements fail.
- Restricted Mean Survival Time (RMST) — Quantify the clinical gain in event-free days despite curve intersection.
- Fleming-Harrington Strike — Apply tail-weighted p-values to capture late-stage divergence.
- Cox Proportional Hazards — Pivot to multivariable modeling to account for baseline severity.
- Stratified Log-Rank — Control for a single categorical confounder while preserving the curve.
Post-hoc
Group mean comparisons and correction controls (e.g. Tukey HSD, Bonferroni) to protect against Family-Wise Error Rates.
Post-hoc pairwise tests defined for this model.
KM post-hoc is an audit of temporal separation. Use RMST to provide a clinically intuitive effect size (days of life gained) alongside the global Log-Rank p-value.
Effect Size
Understanding effect sizes (e.g., Cohen's d, Partial Eta-Squared) and clinical impact benchmarks.
Time at which 50% of the sample has experienced the event of interest. Compare across groups.
Estimated proportion of individuals surviving past a specific time milestone (e.g., 5-year survival rate).
Ratio of hazard rates between groups. HR > 1 indicates higher risk of event in treatment group; HR < 1 indicates lower risk (protective effect).
0.2
0.5
0.8
Sample Size
Guidelines for minimum sample requirements and power analysis parameters.
The 'Event Currency' Mandate: Survival power is 100% dependent on the number of 'Failures' (Events), not just the total N. A minimum of 30 total events is required to stabilize the KM step-curve.
| Effect Size | Parameters | Required n |
|---|---|---|
| Small Effect | HR = 1.5 (Small) | n ≈ 200 total events |
| Medium Effect | HR = 2.0 (Medium) | n ≈ 60 total events |
| Large Effect | HR = 3.0 (Large) | n ≈ 25 total events |
The 'Follow-up' Strike: Power can be increased in two ways: recruit more people or extend the follow-up time. Longer studies accumulate more events, effectively increasing your power without adding new participants.
Reporting
How to compile statistical results into publication prose matching APA and journal style guides.
A Kaplan-Meier survival analysis was conducted to compare the survival distributions of Group A and Group B over time unit. The median survival time was X.XX time unit (95% CI X.XX, X.XX) for Group A and X.XX time unit (95% CI X.XX, X.XX) for Group B. A log-rank test revealed a significant/non-significant difference in survival distributions between the groups, χ²(df) = X.XX, p = .XXX, hazard ratio (HR) = X.XX (95% CI X.XX, X.XX).
- sample size per group and number of events (deaths/failures)
- median survival times with 95% confidence intervals per group
- survival rates at key time milestones (e.g., 1-year, 5-year)
- log-rank statistic (χ²), degrees of freedom, and p-value
- hazard ratio (HR) with 95% confidence interval
Manuscript Lab
Copy standard summary tables and forensic reporting grids to outline analysis details.
| Group | Median Survival | 95% CI | Events | Censored | Log-Rank χ² | p-value |
|---|---|---|---|---|---|---|
| New Protocol | 42.5 | [38.2, 48.5] | 45 | 55 | 12.45 | < .001 |
| Standard Care | 28.4 | [24.1, 32.8] | 72 | 28 | — | — |
The 'Halfway' Marker. The time point at which 50% of the subjects in the group have experienced the event.
The 'Hidden' Data. Participants who either finished the study without the event or dropped out—K-M handles these correctly.
The Curve Comparison. Tests if the entire survival trajectory of Group A is significantly different from Group B.
Command Center
Syntax libraries and function parameters for executing calculations in stats packages.
# 1. Fit Kaplan-Meier Curve
km_fit <- survival::survfit(Surv(time, event) ~ group, data = df)
summary(km_fit)
# 2. Visualize with Risk Table
survminer::ggsurvplot(km_fit, data = df, risk.table = TRUE, pval = TRUE)
# 3. Execute Log-Rank Test
survival::survdiff(Surv(time, event) ~ group, data = df)If survival curves cross, the Log-Rank test is invalid. Always check the 'Proportional Hazards' assumption or use the Gehan-Breslow test for early-time differences.
# Execute Number at Risk Audit
# Ensure you have enough subjects in the 'tail' of the curve to trust the estimates.Common Mistakes
Analytical caveats and corrections to maintain modeling integrity.
References
Scholarly lineage and citation keys grounding the statistical framework.