A yes or no model uses whether an event took place. It does not use when the event took place, or account for cases with no event by the last check. Time-to-event methods can use both. This page shows one of them, the Kaplan-Meier estimator, read at a single landmark: survival at day 90.

Every number on this page comes from a simulated study made by the script below. No patient data is used, and the page reports no finding about people with heart failure.

Worked Example: Survival at Day 90 in Two Simulated Groups

Question: In this made-up study, how did the groups differ in survival by day 90?

Inputs: Simulated data, seed 2026. The script makes 120 cases. Each has a group, a time to the event or to the last check, and an event flag. The study ends at day 180. The groups and event times are made for this example. They are not real patient data. The script sets a rate of 0.004 per day for the lower hazard group and 0.009 per day for the higher hazard group.

Method: The script uses Kaplan-Meier, a way to track survival over time. At each event time, it updates the share still event-free among the cases still at risk. "At risk" means still in the study and able to have the event. If no event is seen by the last check, the case is censored: we know only that no event was seen by then.

import numpy as np

SEED = 2026
N = 120
MAX_FOLLOWUP = 180
rng = np.random.default_rng(SEED)

# Simulated baseline group: 0 = lower hazard, 1 = higher hazard.
group = rng.integers(0, 2, size=N)
rates = np.where(group == 0, 0.004, 0.009)
event_time = rng.exponential(scale=1 / rates)
observed_time = np.minimum(event_time, MAX_FOLLOWUP)
event = (event_time <= MAX_FOLLOWUP).astype(int)

def km_at(times, events, landmark):
    survival = 1.0
    event_times = np.sort(np.unique(times[(events == 1) & (times <= landmark)]))
    for t in event_times:
        at_risk = np.sum(times >= t)
        deaths = np.sum((times == t) & (events == 1))
        survival *= 1 - deaths / at_risk
    return survival

print(f"Simulated data, seed {SEED}")
print(f"Patients: {N}; max follow-up: {MAX_FOLLOWUP} days")
print(
    f"Events by end of follow-up: {int(event.sum())}; "
    f"censored: {int(N - event.sum())}"
)
for label, value in (("lower", 0), ("higher", 1)):
    mask = group == value
    s90 = km_at(observed_time[mask], event[mask], 90)
    print(
        f"{label.capitalize()} hazard group: n={int(mask.sum())}, "
        f"events={int(event[mask].sum())}, "
        f"Kaplan-Meier survival at day 90={s90:.3f}"
    )
overall = km_at(observed_time, event, 90)
print(f"Overall Kaplan-Meier survival at day 90: {overall:.3f}")

Output of python3 -I:

Simulated data, seed 2026
Patients: 120; max follow-up: 180 days
Events by end of follow-up: 77; censored: 43
Lower hazard group: n=64, events=34, Kaplan-Meier survival at day 90=0.688
Higher hazard group: n=56, events=43, Kaplan-Meier survival at day 90=0.518
Overall Kaplan-Meier survival at day 90: 0.608

Result: In this run, 77 cases had an event by day 180 and 43 were censored. Survival at day 90 was 0.688 in the lower hazard group and 0.518 in the higher hazard group. Overall survival at day 90 was 0.608. These values come from the simulated data only.

What is built in: The two rates give a hazard ratio of 2.25 (0.009 divided by 0.004), so a gap between the groups is expected. At those rates, survival at day 90 would be about 0.70 in the lower group and 0.44 in the higher group. The run drew 0.688 and 0.518 because the cases are a random draw. A missed gap would mean low power, not no effect.

Limit: A simple random model made the groups and event times. It does not show what takes place for people with heart failure, and it does not test a cause or a form of care. Every case is followed to day 180 or to its event, so no case is censored before day 90 and the day-90 estimate equals the share still event-free. Censoring before the landmark would change that. The script runs no test and gives no interval.

Sources

Cox published the proportional hazards model in 1972. Kaplan and Meier published their estimator in 1958. Peduzzi and colleagues studied events per variable in Cox models in 1995.

  • Kaplan E, Meier P (1958). Nonparametric Estimation from Incomplete Observations. Journal of the American Statistical Association. doi:10.1080/01621459.1958.10501452. Covers: the Kaplan-Meier estimator.
  • Cox D (1972). Regression Models and Life-Tables. Journal of the Royal Statistical Society Series B: Statistical Methodology. doi:10.1111/j.2517-6161.1972.tb00899.x. Covers: the Cox proportional hazards model.
  • Peduzzi P, Concato J, Feinstein AR, Holford TR (1995). Importance of events per independent variable in proportional hazards regression analysis II. Accuracy and precision of regression estimates. Journal of Clinical Epidemiology. doi:10.1016/0895-4356(95)00048-8. Covers: events per variable in Cox models.

What Makes This a Time-to-Event Problem

The Outcome Is a Time

The outcome is not only whether the event took place. It is how long until it did. Each case contributes a duration and an event flag: an event at day 50 has time 50 and flag 1, and a case with no event by its last check at day 180 has time 180 and flag 0. Together these form the survival object that Kaplan-Meier and Cox models use.

Censoring Is Partial Information

Not every case has the event during the study. A case followed for 180 days with no event tells you that it was event-free for at least 180 days. This is called right-censoring, and it is not missing data. It is partial information that the method keeps by letting the case count as at risk until its last check.

Dropping censored cases throws away information and favours the cases whose events came early. Treating them as event-free forever understates the event rate. Kaplan-Meier avoids both.

Critical Methodological Point

Censoring must be non-informative. The reason a case is censored (study ended, moved away, lost to follow-up) must be unrelated to its risk of the event. If cases drop out because they are getting worse, the estimates are biased. Compare baseline characteristics between censored cases and cases with events to look for signs of this.

The Hazard Is a Rate

The hazard is the instantaneous rate of the event at time t, given the case has lasted up to that point. In the simulated example the rates are constant, 0.004 and 0.009 per day, so the hazard does not change with time there. Real hazards can change over time, and Kaplan-Meier makes no assumption about their shape.

How Kaplan-Meier Works

At time 0 everyone is event-free, so survival is 1.0. At each event time, survival is multiplied by 1 minus the events at that time divided by the cases still at risk. That is the line survival *= 1 - deaths / at_risk in the script above.

A small illustration with made-up counts: suppose 10 cases are at risk at the first event time and one has an event. Survival is multiplied by 1 - 1/10 = 0.9. At the next event time 8 cases are still at risk, because one more case was censored in between, and one has an event. Survival is multiplied by 1 - 1/8 = 0.875. The running product is 0.9 x 0.875 = 0.7875.

The curve is a step function. It drops at each event time and stays flat between events. Censored cases create no step. They only leave the at-risk count, which changes the size of later steps.

Reading a Landmark

A landmark is a fixed time at which you read the curve, here day 90. It gives one number per group: the estimated share still event-free at that time. The full curve shows how the groups move apart or together over time, so look at it as well as the landmark.

A gap between two groups at a landmark is a description. To check whether a gap is larger than chance, use a log-rank test, which checks for a gap between groups. This page does not run one. A group gap alone does not show that a factor causes an event.

Where Cox Regression Comes In

Kaplan-Meier compares groups one factor at a time. A Cox model relates several inputs to the hazard at once:

h(t) = h₀(t) × exp(β₁X₁ + β₂X₂ + ... + βₚXₚ)

Here h₀(t) is the baseline hazard, which can take any shape, and the exponential term scales it. The exponentiated coefficients are hazard ratios. A hazard ratio is a ratio of event rates, not of probabilities. In the simulated example the ratio of the two group rates is 2.25 by construction.

A 95% confidence interval is a range made by a set method. If its rules hold, it would cover the true hazard ratio in 95% of repeat samples.

The proportional hazards assumption says the hazard ratio between two cases stays constant over time. One check uses Schoenfeld residuals. If the rule holds, these leftover values should show no trend over time. They should spread at random around zero. If the rule fails, options include stratifying on the variable or letting its effect change with time.

A Cox model draws its information from the events, not from the number of cases. Count events before you choose how many inputs to include.

Hazard Ratios vs Risk Ratios

A hazard ratio of 2.0 does not mean twice the probability of the event. It means twice the instantaneous rate at any given time. Hazards are rates, not probabilities.

How to Prepare Your Data

Required Data Structure

You need at minimum:

  1. Time variable: The duration from baseline to the event or to censoring, in one consistent unit (days, weeks or months)
  2. Event indicator: 1 if the event took place, 0 if the case was censored
  3. Baseline inputs: Variables measured at or before the start of follow-up

If you have a baseline date and an event or censoring date, calculate time as the difference in days and keep both dates in the same format.

Checking Data Quality

  • No negative times: Time must be 0 or more. A negative value means the date subtraction is backwards or the data has errors.
  • Events have a positive time: A case with an event cannot have it before baseline. A case with flag 0 has a time that is its censoring time.
  • Consistent time units: Do not mix days and months.
  • Inputs measured at baseline: A variable measured during follow-up is time-varying. A standard Cox model assumes baseline inputs only, so time-varying inputs need extended models.

Inputs must come before the event for a cause claim. Timing alone does not show that a factor caused the event.

Your turn

Bring your own data and the question you actually need answered.

MCP AnalyticsData science by email Send us your data and your question, and we will reply. ds@mcpanalytics.ai

Frequently Asked Questions

What makes survival analysis different from logistic regression for event outcomes?

A yes or no model uses whether an event took place. It does not use when the event took place, or account for cases with no event by the last check. Time-to-event methods can use event time and those cases. In the simulated example on this page, 77 of 120 cases had an event by day 180 and 43 did not. Kaplan-Meier uses all 120.

When should I use Kaplan-Meier curves versus Cox regression?

Kaplan-Meier curves answer: what is the survival probability over time for this group? They suit a few distinct groups, such as the lower and higher hazard groups in the example. Cox regression answers: how do several inputs together relate to the hazard? It suits continuous inputs or several factors at once. A common order is to start with Kaplan-Meier to see the pattern, then fit a Cox model.

Why count events, not cases, for a Cox model?

A Cox model draws its information from the events, so the number of events, not the number of cases, limits how many inputs it can support. Peduzzi and colleagues studied events per variable in Cox models in 1995. This page gives no fixed rule. Count your events before you choose how many inputs to include.

What does it mean when the proportional hazards assumption fails?

Cox regression assumes the hazard ratio between groups stays constant over time. When this fails, the effect changes over time. Schoenfeld residuals are one check. If the rule holds, these leftover values should show no trend over time, and they should spread at random around zero. If the rule fails, options include stratifying on the variable or letting its effect change with time.

Can I use survival analysis if not everyone was followed for the same duration?

Yes. Survival methods are built for this. A case followed for 50 days with no event contributes information up to day 50, then is censored. A yes or no model must either drop cases with short follow-up or treat no event at 50 days the same as no event at 180 days.

Related Articles