A Cox model that runs without error and produces a hazard ratio with a tidy p-value has not, by itself, told you anything trustworthy about how the effect behaves over follow-up time. The hazard ratio the model reports is a single number only because the model assumes the ratio of hazards between compared groups (or per unit of a covariate) stays constant across the entire follow-up period — the proportional hazards assumption. If that assumption doesn’t hold, the reported hazard ratio is effectively a weighted average of an effect that is actually stronger early and weaker late (or the reverse, or even reversed in sign), and presenting it as one fixed number misrepresents what the data show. Checking proportionality isn’t an optional diagnostic step tacked onto the end of a survival analysis — it determines whether the headline number in your results table means what you’re about to say it means.
What the Cox model is, and what “proportional hazards” actually asserts
The Cox proportional hazards model, introduced by David Cox in 1972 (“Regression Models and Life-Tables,” Journal of the Royal Statistical Society, Series B), models the hazard — the instantaneous risk of the event occurring at time t, given survival up to t — as:
h(t | X) = h0(t) × exp(β1X1 + β2X2 + … + βpXp)
h0(t) is the baseline hazard — the hazard when all covariates equal zero — and it can take any shape over time; the model doesn’t specify it. That’s what makes Cox regression semi-parametric: the covariate effects (the β coefficients) are estimated parametrically, but the baseline hazard is left unspecified and estimated separately (or not at all, since it cancels out of the partial likelihood Cox used to estimate the β’s without ever having to model h0(t) directly).
The proportional hazards assumption is what that formula implies structurally: because h0(t) is a shared multiplicative factor, the ratio of hazards between any two individuals or groups — exp(β) — is constant across all values of t. It does not assume the hazard itself is constant (it can rise, fall, or do anything over time via h0(t)); it assumes the relative comparison between groups doesn’t change shape over time. That’s the whole assumption, and it’s exactly what a single reported hazard ratio depends on being true.
Three standard ways to check proportionality
1. Log-log survival plot
Plot log(−log(S(t))) against log(t) for each level of a categorical covariate (or for the group split of interest), using the Kaplan-Meier estimate of S(t) within each group. Under proportional hazards, these curves should be roughly parallel — a constant vertical offset between them across the range of t. Curves that visibly converge, diverge, or cross are evidence against proportionality for that covariate. This method is quick and easy to produce alongside a Kaplan-Meier curve, but it’s a visual judgment call, it only works cleanly for categorical (or coarsely-binned continuous) covariates, and different analysts can disagree on what counts as “roughly parallel.” Treat it as a first look, not a final answer.
2. Schoenfeld residuals (the standard formal test)
The Schoenfeld residual test, formalized by Grambsch and Therneau (1994) and implemented as cox.zph() in R (and equivalent functions in SAS, Stata, and Python’s lifelines), is the test researchers are usually asked for. Schoenfeld residuals are computed at each event time, one per covariate, and represent the difference between the observed covariate value for the individual who had the event and the risk-set-weighted average covariate value at that time. Under proportional hazards, the scaled Schoenfeld residuals should show no systematic trend against time — they should scatter around zero with a flat smoothed trend line. The test formalizes this by computing the correlation (rho) between each covariate’s scaled residuals and a transformation of time (rank order, by default in most software), and testing whether that correlation is significantly different from zero, per covariate and globally across the model.
A statistically significant Schoenfeld test for a covariate (conventionally p < 0.05, though this is the same convention-not-law caveat that applies to any single threshold) indicates that covariate’s effect is changing over follow-up time — proportionality is violated for that term. A non-significant result does not prove proportionality holds; it means the test didn’t detect a departure at the available sample size and follow-up length, which is a different claim, especially in smaller studies.
3. Time-varying covariate / interaction-with-time test
A third, related approach adds an explicit interaction term between the covariate and a function of time (commonly log(t), via tt() in R’s survival package) directly into the model, then tests whether that interaction term’s coefficient is significantly different from zero. A significant covariate-by-time interaction is direct evidence that the covariate’s log-hazard effect is not constant over follow-up — the same conclusion the Schoenfeld test reaches, from a different angle. This approach is more flexible (you choose the functional form of the time interaction, which can matter) and doubles as the actual fix if proportionality fails, since the time-varying term can simply stay in the model rather than being reported as a single constant hazard ratio.
Worked example: reading a Schoenfeld test output
Illustrative composite example, not drawn from a real published study — constructed to show what a Schoenfeld test output looks like and how to write it up. Suppose a Cox model estimates the effect of treatment arm, age, and disease stage on time to progression, and the analyst runs cox.zph() on the fitted model:
| Covariate | rho | Chi-square | df | p-value |
|---|---|---|---|---|
| Treatment arm | 0.31 | 8.94 | 1 | 0.003 |
| Age (continuous) | −0.04 | 0.19 | 1 | 0.66 |
| Disease stage (III vs. II) | 0.09 | 1.02 | 1 | 0.31 |
| Global | — | 10.41 | 3 | 0.015 |
The sentence a researcher would write from this table: “The global test of the proportional hazards assumption was significant (χ2 = 10.41, df = 3, p = 0.015), driven by non-proportionality in the treatment-arm effect (p = 0.003); age and disease stage showed no evidence of time-varying effects (p = 0.66 and p = 0.31, respectively). Given the departure for treatment arm, the model was re-fit stratifying by [or: including a treatment-by-log(time) interaction for] treatment arm, and results are reported below under that specification rather than as a single pooled hazard ratio.” That’s the deliverable a proportionality check should produce — not just a pass/fail, but a specific statement of which covariate is affected and what was done about it.
What to do when the assumption fails
A failed proportionality check doesn’t mean the analysis is unsalvageable — it means the single-hazard-ratio summary is the wrong summary for that covariate. Standard options, roughly in order of how often they’re used:
- Stratify by the violating covariate. Using
strata()in R (or the equivalent), the model allows a separate, unspecified baseline hazard for each level of the stratifying covariate while still estimating a common effect for the other covariates. This is the simplest fix when the offending variable is categorical and not itself the effect of primary interest — but it means you no longer get a hazard ratio (or a test) for the stratified variable itself. - Add a covariate-by-time interaction term (the
tt()approach above) and report the effect as time-varying — e.g., “the hazard ratio for treatment was 2.1 in the first 12 months and 0.9 thereafter” — rather than forcing a single pooled number. - Split follow-up time into intervals and fit the model with a step-function time-varying coefficient, which is a coarser, more interpretable version of the same idea.
- Use a different model class where proportionality isn’t assumed: restricted mean survival time (RMST), which summarizes the area under the survival curve up to a fixed horizon and doesn’t require proportional hazards to be interpretable; Aalen’s additive hazards model; or an accelerated failure time (AFT) model (Weibull, log-normal, log-logistic), which models survival time directly rather than the hazard ratio and carries its own, different assumptions.
Whichever option is used, report that the assumption was checked, what was found, and what was done in response — reviewers in fields where survival analysis is routine (oncology, cardiology, epidemiology) increasingly expect this as a matter of course, not an optional appendix note.
Other Cox model assumptions worth checking while you’re at it
Proportional hazards gets most of the attention, but it isn’t the only assumption a Cox model makes:
- Linearity of continuous covariates in the log-hazard. The model assumes each continuous covariate’s effect on the log-hazard is linear. Martingale residuals plotted against a continuous covariate (or the alternative of category-binning, or fitting a restricted cubic spline term) are the standard way to check this — a curved pattern suggests the linear form is wrong.
- Independence of observations. Standard Cox regression assumes one event (or censoring) per independent subject. Repeated events per subject, or subjects clustered within a higher-level unit (hospital, family), need a frailty term or clustered/robust standard errors — using standard Cox regression on clustered survival data without adjustment understates the standard errors.
- Non-informative censoring. The model assumes censoring is unrelated to a subject’s underlying risk of the event, conditional on the covariates in the model. If patients are more likely to drop out precisely because their condition is worsening, censoring is informative and the estimates are biased — this can’t be tested from the survival data alone and has to be reasoned about from how the data were collected.
- No unmeasured confounding, for a causal reading. A hazard ratio from an observational Cox model is confounded by exactly the same logic as any other observational estimate — see confounding variable. Proportionality being satisfied says nothing about whether the effect estimate is causally interpretable.
How this connects to the log-rank test
The log-rank test is the standard nonparametric test for comparing survival curves between two or more groups, most often reported alongside a Kaplan-Meier plot. It’s worth understanding its relationship to the Cox model rather than treating them as unrelated tools: when the only covariate is a single categorical grouping variable, the log-rank test statistic is mathematically equivalent to the score test from a Cox model fit with that one covariate. In other words, a simple two-group Cox model and a log-rank test are, in that specific case, asking the same question with the same underlying machinery — the Cox framework is what lets you extend that comparison to include continuous covariates and multiple predictors simultaneously, and to get an effect-size estimate (the hazard ratio) rather than just a p-value.
The log-rank test carries its own version of the same limitation discussed above: it weights all time points equally and is most powerful when the hazards genuinely are proportional between groups. If survival curves cross — one group better early, the other better later — the log-rank test can return a non-significant result even when there’s a real, clinically meaningful difference at specific time points, because the early and late effects cancel out in the overall statistic. A significant log-rank p-value is good evidence of some difference between groups; a non-significant one does not prove the groups have equivalent survival, particularly if a log-log plot or Schoenfeld check suggests the curves cross rather than run parallel.
Frequently asked questions
What does it mean if the proportional hazards assumption is violated?
It means the effect of that covariate on risk isn’t constant across the follow-up period — it’s stronger at some points in time and weaker (or reversed) at others. The single hazard ratio the model reports for that covariate is a summary that blends those different time periods together, and reporting it as one fixed number can misrepresent how the effect actually behaves.
Is a non-significant Schoenfeld test proof that proportionality holds?
No. It means the test found no evidence of a departure at the sample size and follow-up length available — a failure to detect a violation is not the same as confirming its absence, especially with a small sample or short follow-up. Pair the formal test with the log-log plot or a residual-versus-time scatter as a visual check, particularly in smaller studies.
Does a hazard ratio mean “X times more likely to have the event”?
Not in the way that phrase is usually read. A hazard ratio compares the instantaneous risk of the event at a given moment, conditional on having survived to that moment — it is not a risk ratio or an odds ratio, and it does not translate directly into “X times more likely to die overall.” See how to interpret a hazard ratio for the full distinction, which matters even more once proportionality is in question, since a single hazard ratio is only a faithful summary of the whole follow-up period when the assumption holds.
Can I just always stratify instead of checking the assumption?
Not as a default. Stratifying removes the ability to estimate an effect (or a hazard ratio) for the stratified variable at all, and it’s a real loss of information if that variable is one you actually wanted to report on. Checking first, then choosing the fix that fits which covariate failed and why it matters to your research question, is the more defensible workflow than pre-emptively stratifying everything.
How is the Cox model different from logistic regression for the same data?
Logistic regression models the odds of a binary outcome (event yes/no) without regard to when it happened, treating everyone the same length of follow-up. The Cox model uses the time-to-event information directly and handles censoring (subjects who leave the study, or the study ends, before the event occurs) properly, which logistic regression cannot do without discarding that information or introducing bias. See logistic regression for the binary-outcome case, and use Cox regression whenever the timing of the event, not just its occurrence, is part of the question.
Related reading
- How to interpret a hazard ratio (and why it is not a risk ratio)
- How to read a Kaplan-Meier curve
- Regression analysis: assumptions, interpretation, and how to report it
- Logistic regression (the logit model)
- Multicollinearity and VIF in regression
- How to read a residual plot: checking homoscedasticity
- Absolute risk reduction, relative risk reduction, and NNT
- Cohort study: design, types, and how it works
- Confounding variable
- What is a p-value
- Confidence interval explained







