Introduction
Many clinical-trial endpoints are fundamentally about time.
Investigators may want to know how long patients remain alive, how long they remain free of disease progression, or how long a treatment response lasts.
Examples include:
- Overall survival (OS)
- Progression-free survival (PFS)
- Disease-free survival (DFS)
- Event-free survival (EFS)
- Time to progression (TTP)
- Duration of response (DoR)
These endpoints are commonly analyzed using survival analysis, a family of statistical methods designed for time-to-event data.
In R, one of the foundational tools for this work is the survival package.
Why Ordinary Statistical Methods Are Not Enough
Suppose a clinical trial follows 100 patients for two years.
Some patients die during follow-up. Others remain alive at the end of the study. Still others withdraw, are lost to follow-up, or remain alive when their individual follow-up ends.
For those patients, the exact time of death is unknown.
We only know that they survived for at least the amount of time they were observed.
This is called right censoring.
What Is a Time-to-Event Endpoint?
A time-to-event endpoint contains two fundamental pieces of information:
| Component | Meaning |
|---|---|
| Time | How long the patient was observed until the event or censoring |
| Event indicator | Whether the event actually occurred |
For example:
| Patient | Time | Event |
|---|---|---|
| 001 | 14 months | 1 — death |
| 002 | 24 months | 0 — censored |
| 003 | 9 months | 1 — death |
| 004 | 18 months | 0 — censored |
Patient 002 did not necessarily survive exactly 24 months. We know only that the patient survived at least 24 months without the event being observed.
The Survival Function
The central quantity in survival analysis is the survival function.
It is commonly written as:
where \(T\) is the event time.
In words, \(S(t)\) is the probability of surviving beyond time \(t\).
For example, if:
then the estimated probability of remaining event-free beyond 12 months is 80%.
The Event Indicator
The event indicator is commonly coded:
- 1: event occurred
- 0: right censored
For example:
time status 14 1 24 0 9 1 18 0
This convention is especially convenient when using Surv().
The survival Package
The R package survival provides the core tools
needed for many standard survival analyses.
It includes functionality for:
- Creating survival objects
- Kaplan–Meier estimation
- Survival-curve comparisons
- Cox proportional hazards regression
- Stratified models
- Time-dependent covariates
- Model diagnostics
- Predicted survival quantities
The package is widely used in medical research and clinical-trial analysis.
Loading the Package
library(survival)
If the package has not yet been installed:
install.packages("survival")
The Surv() Function
The most important starting point is the Surv() function.
For right-censored data, the basic form is:
Surv(time, status)
For example:
Surv( time = data$time, event = data$status )
This creates a survival object that can be passed to functions such as survfit() and coxph().
A Small Example Dataset
Consider a simulated oncology study with 12 patients.
trial <- data.frame(
id = sprintf("P%03d", 1:12),
treatment = c(
"Control","Control","Control","Control",
"Control","Control",
"Experimental","Experimental","Experimental",
"Experimental","Experimental","Experimental"
),
time = c(
8, 12, 15, 18, 21, 24,
10, 16, 20, 26, 30, 34
),
status = c(
1, 1, 1, 0, 1, 0,
1, 1, 0, 1, 0, 0
)
)
Here, status = 1 represents the event and status = 0 represents censoring.
Creating the Survival Object
surv_obj <- Surv( time = trial$time, event = trial$status ) surv_obj
The result encodes both the observed time and whether the observation ended with an event or censoring.
Kaplan–Meier Estimation
The Kaplan–Meier estimator is one of the most important tools in survival analysis.
It estimates the probability of remaining event-free over time while accounting for censored observations.
In R, Kaplan–Meier estimation is performed with survfit().
km_fit <- survfit( Surv(time, status) ~ 1, data = trial ) km_fit
The notation:
Surv(time, status) ~ 1
means that we are estimating one overall survival curve without comparing groups.
The Kaplan–Meier Estimator
Suppose event times are:
At each event time, the Kaplan–Meier estimator updates the estimated survival probability.
The estimator can be written as:
where:
- \(d_i\) = number of events at time \(t_i\)
- \(n_i\) = number at risk immediately before \(t_i\)
The resulting curve is a step function.
An Actual Kaplan–Meier Plot
The following simulated figure illustrates two treatment groups.
The curves are simulated for teaching purposes and do not represent an actual clinical trial.
How to Read a Kaplan–Meier Curve
The x-axis represents time.
The y-axis represents the estimated probability of surviving beyond that time.
For example, if a curve is at:
then approximately 75% of the population is estimated to remain event-free beyond 12 time units.
The vertical drops occur at event times.
Censoring does not cause a vertical drop.
What Does a Censoring Tick Mean?
A censoring mark means that the patient's event was not observed after that point of follow-up.
For example, if a patient is censored at 18 months, we know that the patient was event-free through 18 months.
We do not know what happened afterward.
Kaplan–Meier by Treatment Group
To estimate separate curves for treatment groups:
km_fit <- survfit( Surv(time, status) ~ treatment, data = trial ) km_fit
The right-hand side of the formula determines the grouping variable.
Plotting the Kaplan–Meier Estimate
The base R plotting system can directly display a survfit object.
plot( km_fit, xlab = "Time", ylab = "Survival Probability", lwd = 2 )
For clinical reporting, analysts often use ggplot2 or other specialized visualization tools
for greater control over typography, risk tables, confidence intervals, and
annotations.
Confidence Intervals
A Kaplan–Meier estimate is uncertain because it is based on a finite sample.
Confidence intervals can therefore be displayed around the estimated curve.
In survfit(), confidence intervals are available
from the fitted object.
km_fit <- survfit( Surv(time, status) ~ treatment, data = trial, conf.type = "log" )
The exact confidence-interval transformation should be selected according to the statistical analysis specification.
Median Survival
A commonly reported summary is the median survival time.
The median survival is the time at which:
In R:
summary(km_fit)$table
For a group whose survival curve never falls below 50% during observed follow-up, the median may not be estimable.
Restricted Mean Survival Time
When the median is not estimable or when the investigator wants an alternative summary, the restricted mean survival time (RMST) can be useful.
Conceptually, RMST is the area under the survival curve up to a specified time horizon \(\tau\):
RMST can be particularly informative when survival curves do not cross 50% during the available follow-up.
Number at Risk
A Kaplan–Meier curve should generally be interpreted alongside the number of patients remaining at risk.
| Time | Experimental | Control |
|---|---|---|
| 0 | 120 | 118 |
| 6 months | 103 | 91 |
| 12 months | 86 | 69 |
| 18 months | 70 | 48 |
| 24 months | 55 | 31 |
When very few patients remain at risk, the tail of the Kaplan–Meier curve may be based on limited information.
The Log-Rank Test
If the trial compares two or more treatment groups, investigators often use the log-rank test to compare their survival distributions.
In R:
logrank <- survdiff( Surv(time, status) ~ treatment, data = trial ) logrank
The log-rank test evaluates whether the observed event patterns differ between groups under its underlying assumptions.
Interpreting the Log-Rank Test
Suppose the analysis produces:
A small p-value provides evidence against the null hypothesis of equal survival distributions.
It does not, by itself, tell you:
- How large the treatment effect is
- At what time the treatment effect occurs
- Whether proportional hazards holds
- Whether the difference is clinically meaningful
For those questions, effect estimates and graphical displays are important.
Why the Log-Rank Test Is Not Enough
Consider two treatment groups whose survival curves separate only after 18 months.
Another study might show an early difference that disappears later.
A single p-value does not adequately describe these patterns.
Therefore, clinical-trial reporting generally combines:
- Kaplan–Meier curves
- Numbers at risk
- Median survival where estimable
- Hazard ratios
- Confidence intervals
- Statistical tests
- Clinical interpretation
The Hazard Function
Another central concept is the hazard.
The hazard function can be described informally as the instantaneous event rate among patients who have survived to a particular time.
It is commonly represented mathematically as:
The hazard is not the same thing as the probability of an event.
Hazard Ratio
The hazard ratio compares hazards between two groups.
For an experimental treatment compared with control:
under the proportional-hazards interpretation.
For example:
is commonly interpreted as the experimental group having an estimated hazard 70% that of the reference group, corresponding to an approximately 30% lower instantaneous event hazard under the proportional-hazards model.
Hazard Ratio Is Not Risk Ratio
This distinction is essential.
A hazard ratio is not generally equivalent to:
The hazard ratio incorporates the timing of events.
It should therefore not be casually described as a relative reduction in "risk" without appropriate qualification.
The Cox Proportional Hazards Model
The Cox proportional hazards model is one of the most widely used regression models for survival data.
The model is commonly written:
where:
- \(h_0(t)\) is the baseline hazard
- \(X_1,\ldots,X_p\) are covariates
- \(\beta_1,\ldots,\beta_p\) are regression coefficients
The baseline hazard is left unspecified, which is why the Cox model is often called a semi-parametric model.
Fitting a Cox Model in R
The coxph() function fits a Cox proportional
hazards model.
cox_fit <- coxph( Surv(time, status) ~ treatment, data = trial ) summary(cox_fit)
This estimates the coefficient for treatment on the log-hazard scale.
Interpreting the Cox Coefficient
Suppose the fitted coefficient is:
The corresponding hazard ratio is:
Thus:
exp(coef(cox_fit))
returns the hazard ratio.
Confidence Intervals for the Hazard Ratio
A common approach is:
exp(confint(cox_fit))
A complete summary can also be obtained from:
summary(cox_fit)
The resulting table typically contains the coefficient, exponentiated coefficient, standard error, test statistic, and p-value.
Example Hazard-Ratio Interpretation
Suppose a clinical trial reports:
| Measure | Estimate |
|---|---|
| Hazard ratio | 0.72 |
| 95% CI | 0.58–0.89 |
| p-value | 0.002 |
A suitable interpretation would be:
The confidence interval provides information about the precision of the estimated treatment effect.
The Reference Group Matters
If treatment is coded as a factor, R uses one level as the reference category.
For example:
trial$treatment <-
factor(
trial$treatment,
levels = c("Control", "Experimental")
)
The resulting coefficient for Experimental is interpreted relative to Control.
Multiple Covariates
A Cox model can include several prognostic or treatment-related variables.
cox_fit <- coxph(
Surv(time, status) ~
treatment +
age +
stage +
biomarker,
data = trial
)
summary(cox_fit)
This produces adjusted hazard ratios for the included covariates.
Continuous Covariates
Suppose age is entered as a continuous variable.
coxph( Surv(time, status) ~ age, data = trial )
The hazard ratio is then associated with a one-unit increase in age.
If:
the model estimates a 3% higher hazard per one-unit increase in age, assuming the model is otherwise correctly specified.
Sometimes a more interpretable contrast is a 10-year increase.
If the coefficient is \(\beta\), the hazard ratio for a 10-unit increase is:
Categorical Covariates
Categorical variables should generally be explicitly coded as factors.
trial$stage <-
factor(
trial$stage,
levels = c("I", "II", "III", "IV")
)
R then estimates each non-reference category relative to the reference.
Stratification
Sometimes a covariate does not satisfy the proportional-hazards assumption but is important for controlling the baseline hazard.
The Cox model can use stratification:
cox_fit <- coxph(
Surv(time, status) ~
treatment +
age +
strata(region),
data = trial
)
A stratified variable receives a separate baseline hazard for each stratum, while its own coefficient is not estimated.
The Proportional Hazards Assumption
The Cox model assumes that hazard ratios remain proportional over time.
For two patients or groups:
over the relevant follow-up period.
This does not mean that the hazards themselves are constant.
It means their ratio is assumed to be constant.
Testing Proportional Hazards in R
The cox.zph() function is commonly used to assess
the proportional-hazards assumption.
ph_check <- cox.zph(cox_fit) ph_check
A graphical assessment is also useful:
plot(ph_check)
The analysis should not rely exclusively on a single p-value.
Clinical interpretation, graphical diagnostics, and knowledge of the endpoint should all contribute to the assessment.
What If Proportional Hazards Does Not Hold?
If the proportional-hazards assumption is substantially violated, several approaches may be considered.
- Include time-dependent effects
- Stratify on an appropriate variable
- Report time-specific effects
- Use RMST
- Use alternative survival models
- Describe the non-proportional pattern directly
The appropriate strategy depends on the estimand, endpoint, trial design, and statistical analysis plan.
Kaplan–Meier and Cox Models Answer Different Questions
| Method | Primary Purpose |
|---|---|
| Kaplan–Meier | Estimate survival over time |
| Log-rank test | Compare survival distributions |
| Cox model | Estimate covariate effects on hazard |
| RMST | Compare average event-free time through a specified horizon |
These methods complement one another.
Kaplan–Meier vs. Cox Regression
A Kaplan–Meier curve can show:
- How survival changes over time
- Where events occur
- Where censoring occurs
- Whether curves appear to separate
A Cox model can additionally provide:
- Hazard ratios
- Confidence intervals
- Covariate adjustment
- Multivariable modeling
In a clinical study report, the two are frequently presented together.
A Typical Clinical-Trial Workflow
Surv().
Clinical Trial Data Structure
A simplified survival-analysis dataset might look like:
| USUBJID | TRT01P | ADT | ADT_EVENT | AVAL | CNSR |
|---|---|---|---|---|---|
| 001 | Control | 2026-03-15 | 2026-03-15 | 182 | 0 |
| 002 | Experimental | 2026-08-21 | NA | 335 | 1 |
| 003 | Experimental | 2026-06-12 | 2026-06-12 | 265 | 0 |
| 004 | Control | 2026-09-01 | NA | 346 | 1 |
The exact variables depend on the clinical-trial data standard and analysis dataset.
Censoring Indicators in Clinical Trials
Clinical-trial datasets often use a censoring variable rather than directly coding the event as 1.
For example:
CNSR = 0may indicate an eventCNSR = 1may indicate censoring
If that convention is used, the event indicator for Surv() may need to be derived as:
event <- 1 - CNSR Surv( time = AVAL, event = event )
Time Units Matter
Survival time may be expressed in:
- Days
- Weeks
- Months
- Years
The model itself does not require a particular unit.
However, the chosen unit must be consistently defined and clearly documented.
time_months <- (event_date - baseline_date) / 30.4375
The exact derivation should follow the analysis specification rather than an arbitrary conversion chosen during plotting.
Overall Survival
For overall survival, the event is commonly death from any cause.
The survival object might therefore be:
os <- Surv( time = OS_TIME, event = OS_EVENT )
A Kaplan–Meier estimate can then be obtained with:
os_km <- survfit( os ~ TRT01P, data = adtte )
The exact event and censoring rules should come from the prespecified endpoint definition.
Progression-Free Survival
Progression-free survival is more complicated because the event may consist of either:
- Disease progression
- Death
The endpoint derivation therefore typically requires integration of tumor assessment data, clinical events, treatment discontinuation rules, and censoring conventions.
Duration of Response
Duration of response is generally defined only among patients who achieve a prespecified response.
The event may be progression or death, depending on the endpoint definition.
A typical structure is:
The exact definition, however, must follow the protocol and SAP.
Competing Risks
Standard Kaplan–Meier methods can be inappropriate when another event prevents the event of interest from occurring and that competing event is handled as ordinary censoring.
For example, if the endpoint is time to relapse and death without relapse prevents relapse from occurring, death can be a competing event.
Competing-risk analyses may require methods such as cumulative incidence functions rather than treating competing events as non-informative censoring.
Surv() + Kaplan–Meier workflow is not
automatically appropriate for every time-to-event problem. The event
structure should be considered before selecting the statistical method.
Left Truncation and Delayed Entry
Some survival studies include patients who enter the risk set after time zero.
This is sometimes called left truncation or delayed entry.
The survival object can represent entry and exit times:
Surv( time = entry, time2 = exit, event = status )
For example:
Surv( entry_time, exit_time, event )
This tells the analysis that the patient was only under observation and at risk from the entry time onward.
Time-Dependent Covariates
Some covariates change over time.
Examples include:
- Biomarker measurements
- Treatment exposure
- Time-varying disease status
- Transplant status
The Cox framework can accommodate time-dependent covariates, but the dataset must be structured appropriately.
For example:
coxph(
Surv(start, stop, event) ~
treatment +
biomarker,
data = time_varying_data
)
This is substantially more complex than a standard one-row-per-patient analysis and should be specified carefully.
One Row Per Patient Is Not Always Enough
For a basic right-censored endpoint, a dataset may contain one row per patient.
For time-dependent analyses, a patient may contribute multiple observation intervals.
| Patient | Start | Stop | Biomarker | Event |
|---|---|---|---|---|
| 001 | 0 | 90 | Low | 0 |
| 001 | 90 | 180 | High | 0 |
| 001 | 180 | 240 | High | 1 |
The interval structure allows the covariate to change over time.
Stratified Kaplan–Meier Curves
Sometimes the analyst wants curves within another grouping variable.
survfit( Surv(time, status) ~ treatment + stage, data = trial )
This can produce separate curves for combinations of treatment and stage.
For reporting, however, the display should be designed deliberately so that the number of curves does not become excessive.
Confidence Bands in a Kaplan–Meier Plot
Confidence bands can communicate uncertainty around the survival estimate.
A basic base-R display can include confidence intervals:
plot( km_fit, conf.int = TRUE, xlab = "Time", ylab = "Survival Probability", lwd = 2 )
For publication or CSR graphics, analysts often use a more customizable plotting workflow.
Extracting Kaplan–Meier Results
The fitted object contains the estimated survival information.
summary(km_fit)
Useful components include:
km_fit$time km_fit$surv km_fit$n.risk km_fit$n.event km_fit$n.censor
These components can be useful when creating custom tables or figures.
Extracting the Cox Model Results
summary(cox_fit)
Useful quantities include:
coef(cox_fit) exp(coef(cox_fit)) confint(cox_fit) exp(confint(cox_fit))
This allows a programmer to construct a formatted hazard-ratio table.
A Simple Hazard-Ratio Table
A clinical analysis table might contain:
| Covariate | Hazard Ratio | 95% CI | p-value |
|---|---|---|---|
| Experimental vs Control | 0.72 | 0.58–0.89 | 0.002 |
| Age, per 10 years | 1.08 | 0.99–1.18 | 0.081 |
| Stage III vs I/II | 1.54 | 1.16–2.05 | 0.003 |
Forest Plots
Hazard ratios are often presented graphically in a forest plot.
A forest plot makes it easy to see:
- The direction of effects
- The magnitude of hazard ratios
- Confidence intervals
- The reference value of HR = 1
The survival package supplies the model; the actual
forest-plot visualization can be constructed using additional R packages or
custom plotting code.
Adjusted vs Unadjusted Analyses
A treatment-only Cox model:
coxph( Surv(time, status) ~ treatment, data = trial )
produces an unadjusted treatment effect.
A model containing baseline covariates:
coxph(
Surv(time, status) ~
treatment +
age +
stage +
biomarker,
data = trial
)
produces adjusted estimates.
The appropriate primary analysis depends on the prespecified statistical methodology.
Do Not Select Covariates Solely by p-Value
A common mistake is to fit many variables and retain only those with small p-values.
Clinical-trial models should instead follow the prespecified analysis strategy, including decisions about:
- Baseline covariates
- Stratification factors
- Treatment effects
- Interactions
- Missing data
- Model assumptions
Exploratory model selection and confirmatory analysis should not be confused.
Interactions in Cox Models
An interaction can be modeled with:
coxph(
Surv(time, status) ~
treatment * biomarker,
data = trial
)
This evaluates whether the treatment effect varies according to the biomarker.
Interaction analyses require careful interpretation and are especially sensitive to sample size and multiplicity.
Clinical Interpretation of a Hazard Ratio
Suppose:
The estimated hazard is lower in the treatment group.
However, the statement:
"Treatment increases survival by 35%"
is generally too simplistic.
A more accurate description is that the estimated hazard ratio is 0.65, corresponding to an approximately 35% lower hazard under the proportional hazards interpretation.
Survival Probability Is Often Easier to Explain
Suppose the Kaplan–Meier estimates are:
| Time | Experimental | Control |
|---|---|---|
| 12 months | 82% | 71% |
| 24 months | 68% | 51% |
These values can provide an intuitive description of the observed survival experience.
They complement rather than replace the hazard ratio.
Crossing Kaplan–Meier Curves
A particularly important situation occurs when survival curves cross.
For example:
- Treatment A may initially have worse survival.
- The curves may cross after 12 months.
- Treatment A may subsequently have better survival.
This pattern can indicate that a single proportional hazard ratio does not adequately summarize the treatment effect.
Non-Proportional Hazards
Non-proportional hazards can arise from:
- Delayed treatment effects
- Early treatment toxicity
- Immunotherapy-related delayed effects
- Treatment switching
- Changing disease risk over time
In these settings, the hazard ratio may be difficult to interpret as a single summary of the entire follow-up period.
Survival Analysis in Immuno-Oncology
Immunotherapy trials can provide examples where the proportional-hazards assumption deserves particular attention.
Some trials may show:
- Delayed separation of curves
- Early similar event rates
- Later separation
- Long-term survival plateaus
The analysis should therefore consider whether the prespecified estimand and statistical model appropriately capture the treatment effect.
Censoring Mechanisms
Standard survival methods generally rely on assumptions about censoring.
In simplified terms, censoring should not systematically provide information about the future event time after accounting for relevant observed information.
Examples of potentially informative censoring include:
- Patients withdrawing because they are deteriorating
- Patients discontinuing because they are doing particularly well
- Loss to follow-up associated with prognosis
Clinical-trial censoring rules and sensitivity analyses should therefore be prespecified.
Administrative Censoring
Administrative censoring occurs because the study observation period ends before a patient experiences the event.
For example, a patient might remain alive when the database cutoff is reached.
That patient contributes all of their available follow-up without being classified as having experienced the event.
Informative vs Non-Informative Censoring
The distinction is important.
| Type | Concept |
|---|---|
| Administrative | Follow-up ends because the observation period ends |
| Loss to follow-up | Patient cannot be observed after a particular point |
| Potentially informative | Censoring probability may be related to future event risk |
The statistical method does not automatically solve an informative-censoring problem.
Clinical Study Report Considerations
Survival analyses in a CSR typically require clear documentation of:
- Endpoint definition
- Event definition
- Censoring definition
- Analysis population
- Analysis cutoff date
- Time origin
- Time units
- Kaplan–Meier methodology
- Confidence-interval methodology
- Comparison method
- Cox model specification
- Covariate handling
- Missing-data rules
Time Origin Is Critical
The starting point of the survival clock must be clearly defined.
Possible time origins include:
- Randomization
- First dose
- Diagnosis
- Surgery
- Response date
- Transplantation
The appropriate time origin depends on the endpoint.
Analysis Cutoff Dates
Clinical-trial survival analyses are often based on a prespecified data cutoff.
Patients who are still event-free at the cutoff are generally censored according to the endpoint-specific rules.
This means that survival analysis is inherently linked to the timing of the database snapshot.
Example: Building a Kaplan–Meier Analysis
library(survival)
km_fit <- survfit(
Surv(
OS_TIME,
OS_EVENT
) ~ TRT01P,
data = adtte
)
summary(km_fit)
plot(
km_fit,
xlab = "Months from Randomization",
ylab = "Overall Survival Probability",
lwd = 2,
conf.int = TRUE
)
This basic workflow can be expanded into a fully specified clinical-trial analysis.
Example: Cox Analysis
cox_fit <- coxph(
Surv(
OS_TIME,
OS_EVENT
) ~ TRT01P + AGE + SEX + STAGE,
data = adtte
)
summary(cox_fit)
The treatment coefficient provides the adjusted treatment hazard ratio, assuming the model is appropriately specified.
Checking the Cox Model
ph_test <- cox.zph(cox_fit) print(ph_test) plot(ph_test)
The output should be reviewed together with the Kaplan–Meier curves and the scientific context.
Creating a Survival Analysis Dataset
A robust programming workflow separates endpoint derivation from statistical modeling.
Surv().
Validation of Event Dates
For each patient, the programmer should be able to answer:
- What was the time origin?
- What was the event date?
- If there was no event, what was the censoring date?
- Why was the patient censored?
- Was the event date after the time origin?
- Does the analysis time agree with the source dates?
A survival curve can be mathematically correct while still being clinically wrong if the endpoint derivation is incorrect.
Common Data-Programming Mistakes
- Reversing the event indicator. This can turn events into censored observations and vice versa.
- Using the wrong time origin. All subsequent survival times become misaligned.
- Ignoring endpoint-specific censoring rules. Different endpoints can require different handling of events and follow-up.
- Using the database cutoff incorrectly. This can produce incorrect censoring dates.
- Mixing time units. Days, months, and years should not be mixed without explicit conversion.
- Ignoring competing events. Some endpoints require competing-risk methods rather than ordinary Kaplan–Meier estimation.
- Reporting an unstable tail. Very few patients at risk can make late estimates highly uncertain.
- Ignoring proportional-hazards diagnostics. A Cox model should not automatically be interpreted as a constant treatment effect.
Kaplan–Meier Curve vs. Cumulative Incidence
These displays answer different questions.
| Display | Primary Quantity |
|---|---|
| Kaplan–Meier | Probability of remaining event-free |
| Cumulative incidence | Probability of experiencing a particular competing event by time t |
The distinction becomes important in competing-risk settings.
Survival Probability vs. Cumulative Event Probability
For a simple single-event setting:
Thus, if:
then the estimated probability of experiencing the event by 12 months is approximately:
or 20%, under the corresponding single-event framework.
Why the Kaplan–Meier Curve Is a Step Function
The Kaplan–Meier estimator changes only when an event occurs.
Censoring changes the number at risk but does not itself reduce the estimated survival probability.
Therefore, the curve:
- Steps downward at events
- Remains flat between events
- Does not drop at censoring times
An Example of the Risk Set
Suppose there are 100 patients at risk immediately before an event time, and two events occur.
The survival estimate is multiplied by:
If the survival estimate immediately before the event was 0.90, then after the event:
The curve therefore falls to approximately 88.2%.
Multiple Events at the Same Time
If multiple events occur at the same time, the Kaplan–Meier estimator accounts for all of them at that event time.
This is one reason why understanding the risk set is important when validating survival estimates.
Survival Analysis and Ties
In clinical-trial data, multiple patients may experience events on the same day.
The Cox model therefore needs a method for handling tied event times.
The coxph() function supports established
approaches for ties, and the analysis should use the method specified by the
statistical methodology.
Reproducible R Code
A clean survival-analysis program should separate:
- Data preparation
- Endpoint derivation
- Statistical modeling
- Figure generation
- Output formatting
- Quality control
For example:
# 1. Prepare analysis data adtte <- analysis_data # 2. Create survival object os_surv <- with( adtte, Surv(OS_TIME, OS_EVENT) ) # 3. Kaplan-Meier model km_fit <- survfit( os_surv ~ TRT01P, data = adtte ) # 4. Cox model cox_fit <- coxph( os_surv ~ TRT01P + AGE + SEX, data = adtte ) # 5. Diagnostics ph_check <- cox.zph(cox_fit) # 6. Review outputs summary(km_fit) summary(cox_fit) print(ph_check)
Using Formula Syntax Correctly
R survival models use formula notation.
For example:
Surv(time, status) ~ treatment
can be read as:
The left side contains the survival outcome. The right side contains predictors or grouping variables.
Multiple Groups
Kaplan–Meier estimation can accommodate more than two groups.
km_fit <- survfit( Surv(time, status) ~ dose_group, data = trial )
The resulting object contains one survival curve per group.
If there are many groups, the figure can quickly become difficult to read.
Stratified Randomized Trials
Some clinical trials use stratified randomization.
The Cox model may incorporate those stratification factors according to the prespecified analysis.
cox_fit <- coxph(
Surv(time, status) ~
treatment +
strata(randomization_factor),
data = trial
)
The exact model should follow the SAP rather than being chosen after inspecting the results.
Missing Baseline Covariates
Missing covariates can cause patients to be excluded from a complete-case Cox model unless missingness is explicitly handled.
For example:
coxph(
Surv(time, status) ~
treatment + biomarker,
data = trial
)
may use only observations with complete values for the variables included in the model.
Landmark Analyses
Sometimes investigators want to examine survival after a defined landmark time.
For example, patients might be classified according to response status at Week 12 and then followed thereafter.
Such analyses require careful handling of selection and guarantee-time bias.
They should be prespecified or clearly labeled as exploratory.
Immortal Time Bias
A related issue is immortal time bias.
This can occur when patients must survive event-free for a period before they can be classified into an exposure group, but that guaranteed survival period is not handled correctly in the analysis.
Time-dependent methods or landmark approaches may sometimes be more appropriate.
Clinical Interpretation of Kaplan–Meier Curves
When reviewing a Kaplan–Meier figure, ask:
- What is the time origin?
- What is the event?
- How many events occurred?
- How much censoring occurred?
- How many patients remain at risk?
- Do the curves separate early or late?
- Do the curves cross?
- Is the median estimable?
- Is the tail based on many or few patients?
Clinical Interpretation of Cox Results
When reviewing a hazard ratio, ask:
- Which group is the reference?
- What is the hazard ratio?
- What is the confidence interval?
- Does the confidence interval include 1?
- Was the model adjusted?
- Were the proportional-hazards assumptions evaluated?
- Is the estimated effect clinically meaningful?
- Does the HR adequately summarize the observed curve pattern?
Statistical Significance vs. Clinical Significance
A small p-value does not necessarily imply a clinically important treatment effect.
For example:
could be statistically significant in a very large trial while representing a relatively modest treatment effect.
Conversely, a clinically important hazard ratio may have a wide confidence interval in a small study.
Effect size, precision, and clinical context should therefore be considered together.
A Practical Reporting Framework
| Component | What It Communicates |
|---|---|
| Kaplan–Meier curve | Longitudinal survival experience |
| Number at risk | Amount of information remaining over time |
| Median survival | Time at which estimated survival reaches 50%, if estimable |
| Hazard ratio | Relative event hazard under the model |
| 95% CI | Precision of the effect estimate |
| Log-rank p-value | Statistical comparison of survival distributions |
| RMST | Average event-free time through a specified horizon |
Survival Analysis Quality Control
Survival-analysis programming should undergo independent validation or other appropriate quality-control procedures.
At minimum, verify:
- Correct analysis population
- Correct treatment assignment
- Correct time origin
- Correct event date
- Correct censoring date
- Correct event indicator
- Correct analysis time
- Correct time units
- Correct Kaplan–Meier estimates
- Correct number-at-risk counts
- Correct median survival estimates
- Correct hazard ratios
- Correct confidence intervals
- Correct p-values
- Correct reference category
- Correct model covariates
- Appropriate assessment of proportional hazards
Patient-Level Validation
A useful QC strategy is to trace individual patients through the survival derivation.
For selected patients, verify:
| Patient | Time Origin | Event/Censor Date | Time | Event |
|---|---|---|---|---|
| 001 | Day 0 | Day 180 | 180 | 1 |
| 002 | Day 0 | Day 365 | 365 | 0 |
| 003 | Day 0 | Day 240 | 240 | 1 |
This type of patient-level review can detect derivation errors that may not be obvious from the final Kaplan–Meier curve.
Cross-Checking Kaplan–Meier Output
For important outputs, independently verify selected survival estimates.
For example, confirm that:
- The survival probability changes only at event times.
- Censoring does not directly cause a drop.
- The number at risk decreases appropriately.
- The reported median corresponds to the curve crossing 0.50.
Common Mistakes With Surv()
- Using the wrong event coding. Always confirm what 0 and 1 mean.
- Passing dates directly without defining analysis time. The survival model should receive a meaningful time scale.
- Using event date as time without subtracting the time origin. The time variable must represent elapsed follow-up.
- Ignoring delayed entry. Some datasets require start-stop survival objects.
- Assuming all endpoints use the same censoring rule. OS, PFS, DoR, and other endpoints can have different definitions.
Common Mistakes With Kaplan–Meier Curves
- Ignoring the number at risk. Late curves can be based on very few patients.
- Overinterpreting crossing curves. A single hazard ratio may not adequately summarize non-proportional effects.
- Reporting a median when it was not reached. The median is not automatically the final follow-up time.
- Confusing censoring with an event. Censored patients remain part of the risk-set calculation up to their censoring time.
- Using the wrong time origin. This changes the endpoint itself.
Common Mistakes With Cox Models
- Interpreting HR as a risk ratio. The hazard ratio describes relative hazard, not simply cumulative event probability.
- Ignoring the reference group. The direction of the HR depends on which group is the reference.
- Assuming proportional hazards automatically holds. It should be assessed.
- Using post hoc variable selection for a confirmatory model. The model should follow the prespecified analysis strategy.
- Reporting only the p-value. The effect estimate and confidence interval are essential.
Survival Analysis in a Statistical Analysis Plan
An SAP should clearly describe the planned survival analysis.
Important components may include:
- Endpoint definition
- Time origin
- Event definition
- Censoring rules
- Analysis population
- Kaplan–Meier methodology
- Confidence intervals
- Median survival estimation
- Treatment comparison
- Cox model specification
- Covariates
- Stratification
- Proportional-hazards assessment
- Sensitivity analyses
Example SAP Language
The actual wording should reflect the trial-specific estimand and SAP.
Survival Curves Are Not Just "Lines"
A Kaplan–Meier figure contains substantial statistical information.
The curve reflects:
- Event timing
- Risk-set sizes
- Censoring
- Accumulated survival probability
This is why simply connecting observed event proportions with ordinary lines would not produce a valid Kaplan–Meier estimate.
Why Censoring Does Not Cause a Drop
Suppose a patient is censored at 10 months.
That patient was event-free through month 10.
Therefore, the patient's information contributes to the risk set before 10 months.
After censoring, the patient is removed from the risk set.
But because no event occurred, the survival probability does not drop at that time.
An Intuitive Example
Suppose five patients are followed:
| Patient | Time | Status |
|---|---|---|
| A | 3 | Event |
| B | 5 | Censored |
| C | 7 | Event |
| D | 10 | Event |
| E | 12 | Censored |
At month 3, five patients are initially at risk and one event occurs.
The survival estimate becomes:
At month 5, Patient B is censored. There is no survival drop.
At month 7, another event occurs among the remaining patients, producing the next drop.
Survival Analysis and Reproducibility
The survival package makes the statistical
analysis reproducible because the same analysis can be regenerated directly
from the analysis dataset and R code.
A reproducible workflow should retain:
- Source data version
- Analysis dataset
- Endpoint derivation code
- Statistical model code
- Figure code
- Output version
- Software/package versions where required
Minimal End-to-End Example
library(survival) # Kaplan-Meier analysis km <- survfit( Surv(time, status) ~ treatment, data = trial ) summary(km) # Treatment comparison logrank <- survdiff( Surv(time, status) ~ treatment, data = trial ) logrank # Cox proportional hazards model cox <- coxph( Surv(time, status) ~ treatment, data = trial ) summary(cox) # Hazard ratio exp(coef(cox)) # 95% CI exp(confint(cox)) # Proportional hazards diagnostic cox.zph(cox)
A More Complete Clinical-Trial Workflow
library(survival)
# -------------------------------
# Analysis endpoint
# -------------------------------
adtte$event <- 1 - adtte$CNSR
adtte$time <-
adtte$AVAL / 30.4375
# -------------------------------
# Kaplan-Meier
# -------------------------------
km_fit <- survfit(
Surv(time, event) ~ TRT01P,
data = adtte
)
# -------------------------------
# Log-rank comparison
# -------------------------------
logrank <- survdiff(
Surv(time, event) ~ TRT01P,
data = adtte
)
# -------------------------------
# Cox model
# -------------------------------
cox_fit <- coxph(
Surv(time, event) ~
TRT01P +
AGE +
SEX +
STAGE,
data = adtte
)
# -------------------------------
# Hazard ratios
# -------------------------------
hr <- exp(coef(cox_fit))
ci <- exp(
confint(cox_fit)
)
# -------------------------------
# PH assessment
# -------------------------------
ph <- cox.zph(cox_fit)
print(ph)
What the survival Package Does Not Do Automatically
The package provides statistical methods, but it does not know your clinical endpoint definition.
It does not automatically determine:
- What constitutes progression
- Which death dates belong to the endpoint
- Which assessments qualify as valid
- When censoring should occur
- What the analysis cutoff should be
- Which population belongs in the analysis
Statistical Programming vs. Clinical Endpoint Derivation
This distinction is particularly important in clinical trials.
A programmer may write:
survfit( Surv(AVAL, EVENT) ~ TRT01P, data = adtte )
perfectly.
If EVENT or AVAL is incorrectly derived, however, the final
result remains incorrect.
Therefore, survival-analysis quality depends on both:
- Correct endpoint derivation
- Correct statistical implementation
Interactive Figure: What the Curve Is Telling You
The following simplified figure emphasizes the relationship between survival probability and follow-up.
The downward steps correspond to events. Censoring marks indicate the end of individual follow-up without an observed event.
Median Survival From the Figure
The median is the time at which the estimated survival curve first reaches or falls below 50%.
If the curve never reaches 50%:
then the median is not estimable from the observed follow-up.
A Second Important Survival Concept: The Tail
The right-hand tail of a Kaplan–Meier curve deserves special attention.
Imagine a study beginning with 500 patients. At 36 months, perhaps only 20 patients remain at risk.
The estimated curve may still be mathematically valid, but the amount of information supporting the estimate is much smaller.
This is why number-at-risk tables are essential.
Why a Large Hazard Ratio CI Matters
Suppose:
The point estimate suggests lower hazard, but the confidence interval is wide and includes 1.
This indicates substantial uncertainty about the treatment effect.
The point estimate should not be interpreted in isolation.
Sample Size and Number of Events
Survival-analysis precision is often strongly influenced by the number of observed events.
A trial can have many enrolled patients but relatively few events.
In that situation:
- Confidence intervals may be wide.
- Median survival may not be reached.
- Late Kaplan–Meier estimates may be unstable.
- Cox-model estimates may be imprecise.
The number of events should therefore be considered alongside the total sample size.
Survival Analysis and Interim Analyses
In event-driven trials, interim analyses may occur after a specified number of events rather than at a fixed calendar date.
This creates additional considerations for:
- Database cutoff
- Event counts
- Information fraction
- Statistical boundaries
- Multiplicity
The basic survival functions can calculate the
survival estimates, but interim-analysis methodology requires additional
statistical planning.
Survival Analysis and Treatment Switching
Treatment switching can complicate interpretation of overall survival.
If patients assigned to control later receive experimental treatment, the observed survival comparison may no longer represent the treatment effect under simple intention-to-treat interpretation.
Potential approaches include prespecified sensitivity analyses and specialized methods for treatment-switching adjustment.
These analyses should not be improvised solely during programming.
Survival Analysis and Multiplicity
A trial may evaluate several time-to-event endpoints:
- Overall survival
- Progression-free survival
- Event-free survival
- Time to response
- Duration of response
If multiple confirmatory hypotheses are tested, multiplicity control may be required.
The survival package does not decide the
multiplicity strategy.
How Survival Analysis Fits Into a Clinical Reporting Package
Recommended Survival-Analysis Checklist
Surv() object.
Key R Functions to Remember
| Function | Purpose |
|---|---|
Surv() |
Create a survival response object |
survfit() |
Estimate Kaplan–Meier survival curves |
survdiff() |
Perform a log-rank-type comparison |
coxph() |
Fit a Cox proportional hazards model |
cox.zph() |
Assess proportional-hazards assumptions |
summary() |
Inspect fitted survival and Cox-model results |
confint() |
Obtain confidence intervals for model coefficients |
The Most Important Concept
The most important idea in survival analysis is that time and event status must be analyzed together.
A patient who experiences an event after 24 months and a patient who is censored after 24 months are not equivalent observations.
Both contribute 24 months of follow-up, but only the first contributes an observed event at 24 months.
Survival-analysis methods are designed specifically to preserve this distinction.
survival package provides the core R tools for
many standard time-to-event analyses. The fundamental workflow is to create a
validated Surv() object, estimate Kaplan–Meier
curves with survfit(), compare groups using an
appropriate prespecified method, and estimate covariate effects with coxph(). In clinical trials, however, the most
important work often occurs before these functions are called: correctly
defining the endpoint, time origin, event, censoring rules, and analysis
population. The statistical model can only be as correct as the endpoint data
provided to it.
Summary
Survival analysis provides a framework for analyzing time-to-event endpoints in the presence of censoring.
The survival package makes the core methodology accessible through a small number of powerful functions.
The essential workflow is:
For clinical-trial statisticians, the key concepts to master are:
- Right censoring
- Risk sets
- Kaplan–Meier estimation
- Median survival
- Number at risk
- Log-rank testing
- Hazard functions
- Hazard ratios
- Cox proportional hazards regression
- Proportional-hazards diagnostics
- Non-proportional hazards
- Endpoint-specific censoring
- Clinical-trial validation and traceability
Once these concepts are understood, the R syntax becomes comparatively straightforward.
References
Therneau, T.M. (2024). A Package for Survival Analysis in R.
R package documentation for survival.
Therneau, T.M. and Grambsch, P.M. (2000). Modeling Survival Data: Extending the Cox Model.
Springer.
Klein, J.P. and Moeschberger, M.L. (2003). Survival Analysis: Techniques for Censored and Truncated Data.
Springer.
Collett, D. (2015). Modelling Survival Data in Medical Research.
CRC Press.
Cox, D.R. (1972). Regression Models and Life-Tables. Journal of the Royal Statistical Society: Series B, 34(2),
187–220.
Kaplan, E.L. and Meier, P. (1958). Nonparametric Estimation from Incomplete Observations. Journal of the American Statistical Association, 53(282),
457–481.