Home / Tools / coxph Output Interpreter

coxph() Output Interpreter

A Cox model prints four blocks, and the one everybody reads is the one that answers the smallest question. Paste the output of summary(model) from survival::coxph() and this labels every region, turns each hazard ratio into a sentence about two patients, says what an interval crossing 1 does and does not prove, and points at the assumption the printout never checks.

I want to

Try a real coxph summary:
Paste summary(model) here

Copy the whole summary, the Call line through the last test. Keep the spacing as R printed it.

Waiting for a paste

Paste a coxph summary to decode it

Or load one of the three real model summaries above.

How this is computed
1Paste a coxph summary and this fills in with your model's own numbers.
2
3
4
The same reading, from the model object in R

Nothing you paste leaves your browser Parsing and arithmetic verified against R's survival::coxph() over 16 real fits Free, no sign-up

The block everyone reads, and the block worth reading

The coefficient table answers "is it significant". The interval table answers "how big is it, and how sure are we". Only one of those is a finding.

A Cox model does something clever: it never models the baseline hazard at all. It refuses to say how likely the event is at any particular time, and only ever says how much more or less likely it is for one patient than another. That refusal is why the model works on almost any survival data without you having to claim the events follow a Weibull or an exponential curve, and it is why every number it gives you is a ratio rather than a risk.

So the coefficient is on the log-hazard scale, which nobody has intuition for, and exp(coef) puts it back on the ratio scale, which everybody does. That single exponential is the whole trick. A coef of -0.5310 is meaningless on sight; exp(-0.5310) = 0.588 says the hazard is cut by 41 percent, and that is a sentence a clinician can act on.

The p-value in the coefficient table is a Wald test: it asks whether the coefficient is distinguishable from zero, which after exponentiating is the same as asking whether the hazard ratio is distinguishable from 1. It is the least informative number in the block. It tells you nothing about size, nothing about direction beyond the sign you can already see, and nothing about whether the model is any good. The interval tells you all three.

What a hazard ratio is not

The hazard is an instantaneous rate. It is not a probability, and the ratio is not a risk ratio.

A hazard ratio of 2 does not mean twice as many patients die. It does not mean anyone dies twice as fast, and it does not mean survival time is halved. It means that at any instant, among patients who have made it that far, the rate at which the event arrives is doubled. Over a whole study a hazard ratio of 2 typically produces far less than twice the deaths, because both groups' survival curves bend and the pool of patients still at risk shrinks differently in each.

This matters most when someone asks the obvious follow-up: so how much longer do they live? The Cox model does not answer that, on purpose. It never modelled the baseline hazard, so it has no absolute times to give you. If the question is about time or absolute risk rather than relative rate, you need survfit(model, newdata = ...), which combines the hazard ratios with an estimated baseline to produce actual survival curves for actual patients. The hazard ratio is the model's finding; the survival curve is what a patient would want to be told.

One more trap: a hazard ratio is only interpretable relative to the other terms in the model. It is the effect of that covariate among patients alike on everything else the model knows about. Add a covariate and every other hazard ratio can move. That is not instability, it is the definition.

The assumption in the name

Cox proportional hazards. The second half of that name is a claim, and the printout never checks it.

Every hazard ratio in the output is a single number applied to the entire follow-up. The model asserts that the effect on day 1 equals the effect on the last day. That is a strong claim about the world, and a great many real effects break it. Surgery raises the hazard sharply for a fortnight and lowers it afterwards. A drug works for a year and then stops. Two survival curves cross, which is the clearest violation there is, and the model responds by reporting an average hazard ratio near 1, hiding a large effect in each direction.

Nothing above tells you which case you are in. The check lives in a separate function:

StepCommandWhat it tells you
Testcox.zph(model)One row per covariate plus GLOBAL. A small p means that covariate's effect moves over time
Lookplot(cox.zph(model))A flat line means proportional. A drift or a crossing shows you the shape of the violation
Stratifystrata(x)Lets each level have its own baseline hazard. Use when you do not need x's hazard ratio
Time-varytt(x)Lets the coefficient change with time. Use when you do need x's effect

Like any hypothesis test, cox.zph() has power that scales with your event count. Few events and it misses real violations; tens of thousands and it flags violations too small to matter. Read the plot next to the p-value, not instead of it.

Concordance, and why it is the number to quote

A model can be built entirely of significant coefficients and still be useless at ranking patients.

Concordance takes every pair of patients whose survival can be ordered and asks how often the model gets the order right. That is a question about usefulness rather than about statistical detectability, and the two come apart constantly. With enough events, a covariate that shifts the hazard by 3 percent will have a tiny p-value and change nobody's prognosis. The p-value scales with sample size; concordance does not.

Read it like this: 0.5 is a coin flip, because a coin flip gets half of all orderings right. 0.6 is weak but real. 0.7 is a genuinely useful model. Anything above 0.9 on real survival data is rare enough that the first thing to check is whether a covariate is leaking information about the outcome, such as a treatment given only to patients already known to be dying.

Common questions

What is the hazard ratio in a Cox model?

It is exp(coef), and nothing more. The coefficient is on the log-hazard scale, so exponentiating it gives the factor by which the hazard is multiplied for a one-unit increase in that covariate, holding every other term in the model fixed. A hazard ratio of 1.7 means 70 percent more risk of the event at any given instant; 0.59 means 41 percent less. The hazard is an instantaneous rate rather than a probability, so a hazard ratio of 2 does not mean twice as many deaths by the end of the study.

What does it mean when the confidence interval includes 1?

A hazard ratio of 1 is the value that means no effect: the hazard is multiplied by one, so nothing changes. If the interval includes 1, the data are consistent with no effect at all, and usually with effects in both directions too. That is not evidence the covariate does nothing. It means this study, with this many events, cannot separate it from nothing. A wide interval around 1 and a narrow interval around 1 are very different findings even though both cross the line.

What is concordance in a Cox model?

It is the c-statistic: take any two patients whose survival times can be ordered, and concordance is the proportion of those pairs the model ranks correctly. 0.5 is a coin flip, 1.0 is perfect. It is the closest thing a Cox model has to an accuracy score, and it answers a different question from the p-values. A model can be full of tiny p-values and still rank patients barely better than chance, which is worth knowing before anyone uses it to make decisions about people.

Why does coxph print three global tests, and which one do I report?

The likelihood ratio, Wald and score tests all ask the same question: is this model better than one with no covariates at all? They are asymptotically equivalent, meaning they agree in large samples, which is why they usually land in the same place. Report the likelihood ratio test. It has the best small-sample behaviour of the three and is the one most reviewers expect.

What if the three global tests disagree?

First check whether they disagree on the conclusion or merely on the statistic. Statistics of 210, 223 and 279 look far apart but all give p below 1e-44, so they agree completely and there is nothing to resolve. A real split, where one is significant and another is not, points at a small number of events, a covariate on a wildly different scale from the others, or near-separation where one group has almost no events. Trust the likelihood ratio test and investigate why the others drifted. If the model used a robust or clustered variance, a gap is expected: the Wald test uses the robust standard errors and the other two do not, which is what the note R prints underneath is telling you.

What is the proportional hazards assumption?

It is the assumption that each hazard ratio is constant for the whole follow-up: the same number on day one and on the last day. Nothing in the printed summary tests it. The coefficients, p-values, concordance and global tests all look identical whether it holds or not. Check it with cox.zph(model), which correlates scaled Schoenfeld residuals with time, and read plot(cox.zph(model)) alongside the p-value. If it is violated, stratify the offending covariate with strata() when you do not need its hazard ratio, or fit a time-varying coefficient with tt() when you do.

Why is the confidence interval not symmetric around the hazard ratio?

Because the interval is built on the log-hazard scale and then exponentiated. R computes coef plus or minus 1.96 times the standard error, which is symmetric around the coefficient, and then takes exp of both ends. Exponentiating stretches the upper end and compresses the lower one, so the interval is symmetric in log space and asymmetric in hazard-ratio space. That is correct and expected; an interval that looked symmetric around the hazard ratio would be the suspicious one.

What is exp(-coef) in the output?

It is the reciprocal of the hazard ratio, and it exists purely to save you the arithmetic of reading the effect in the other direction. If exp(coef) is 0.588 for sex, then exp(-coef) is 1.701: the reference group carries 1.7 times the hazard of the comparison group. It is the same finding stated the other way round, not a second result.

Why does n not match my dataset?

Because coxph drops any row with a missing value on any variable in the model, and it says so in the line under the counts: for example, 14 observations deleted due to missingness. The n reported is what was actually fitted. Note also that the number governing your standard errors is the event count, not n. A study with 1000 subjects and 12 events has roughly as much information about a coefficient as a study with 20 subjects and 12 events.

Go deeper

The survival material on this site, in the order that makes sense after this page.

PageWhy next
Cox proportional hazardsThe model behind this output, built up from the partial likelihood
Checking proportional hazardscox.zph, Schoenfeld residuals, and what to do when the assumption fails
Kaplan-Meier and the log-rank testThe survival curve a hazard ratio is relative to
Survival analysis courseCensoring and survival data from the start, in order
Survival analysis exercises18 practice problems on real datasets
Survival power calculatorHow many events a hazard ratio of this size needs before a study can see it
lm() output interpreterThe same treatment for a linear model summary
glm() output interpreterOdds ratios, the other coefficient people exponentiate