Competing risks — cumulative incidence and Fine-Gray
Why 1 − KM always overstates risk when competing events exist, what the cumulative incidence function actually counts, how cause-specific and subdistribution hazards answer two different questions, and why age points in opposite directions in the two models fitted to the MGUS cohort.
What this method is solving
survival::mgus2 is the Mayo Clinic cohort of patients with MGUS (monoclonal gammopathy of undetermined significance): 1384 patients, median age 72 at diagnosis, followed for up to 35 years.
The clinical question is: what proportion of patients with MGUS eventually progress to a plasma cell malignancy — multiple myeloma and its relatives? That number decides whether to follow these patients at all, and how often.
Here is how the observed outcomes actually distribute:
| Outcome | Patients |
|---|---|
| Progressed to plasma cell malignancy | 115 |
| Died of another cause without progressing | 860 |
| Alive and progression-free at the end of follow-up | 409 |
Look at the problem: deaths from other causes (860) outnumber progressions (115) by a factor of 7.5. This cohort is old at diagnosis, and most of them died of cardiovascular disease, another cancer or something else before the MGUS ever had the chance to progress.
A patient who has already died of a myocardial infarction cannot go on to develop myeloma. That is a competing event: it does not interrupt your observation, it permanently removes the possibility of the event of interest. This is the fundamental difference between censoring and a competing event.
Why 1 − KM overstates
The whole logic of Kaplan-Meier rests on non-informative censoring: someone who is censored is “still out there, we just stopped seeing them”, and they remain capable of having the event at the same rate as the people still under observation.
Treating death as censoring and running KM therefore assumes: those patients who died would, if we had kept following them, have progressed to myeloma at the same rate as the living. That is not possible.
⚠️ Note that this assumption belongs to the 1 − KM estimator, not to the act of censoring itself. A cause-specific hazard is by definition conditional on the people still at risk at that instant; it never claims anything about the future of those who died — which is why censoring competing events to estimate one is legitimate. The section on the two hazards returns to this.
The result is that 1 − KM systematically counts the dead patients’ imaginary progressions. This is not a small bias:
| Timepoint | Still at risk | CIF, progression (correct) | CIF, death from other causes | 1 − KM (wrong) | Overstatement |
|---|---|---|---|---|---|
| 5 years | 874 | 3.4% | 32.0% | 4.2% | 1.24× |
| 10 years | 424 | 6.4% | 53.2% | 9.5% | 1.49× |
| 20 years | 57 | 10.0% | 72.4% | 21.0% | 2.10× |
| 30 years | 3 | 13.4% | 78.4% | 42.5% | 3.17× |
At 20 years the correct answer is 10.0% while 1 − KM claims 21.0% — more than double. By 30 years it is worse still (3.17-fold), because by then the risk set is down to 3 people.
The “died of other causes” column is where the overstatement comes from. At the same 20-year mark, 72.4% of the cohort had already died of something else, and none of them can progress to malignancy any more. 1 − KM treats them as “still waiting to progress, just unobserved”, and so it inflates a proportion that belongs to the genuinely still-at-risk minority into a proportion of the whole cohort.
Add the three pieces up and the property becomes visible: progression 10.0% + death from other causes 72.4% + alive and progression-free 17.6% = 100%. Everyone must land in exactly one of those three boxes, which follows from the definition of the CIF. 1 − KM has no such property.
The cumulative incidence function
The cumulative incidence function (CIF) answers a direct question: by time , what proportion of people have actually had the event from cause 1?
Taken apart: is the instantaneous rate of having an event from cause 1 at instant , and is the probability of surviving to with no event of any kind yet. Multiply, then accumulate over time.
is the crucial term: it is the overall event-free survival probability, pushed down by all causes together. So when competing events are common, falls fast, fewer and fewer people remain able to progress, and naturally climbs slowly. The reason 1 − KM is wrong is precisely that it replaces with a survival probability that considers cause 1 only — a world in which nobody dies of anything else.
figures/scripts/B3-05-competing-risk.RComparing groups: a split CIF and Gray’s test
Both figures so far show one curve. Figure 1 of a real paper almost never does — it shows a CIF split by group with a p-value beside it. Readers usually arrive here knowing that 1 − KM must not be reported as a cumulative incidence, but not knowing where the p-value beside a pair of CIF curves is supposed to come from — a log-rank test is testing something else. This section is that missing box.
Split the same mgus2 cohort by age at diagnosis:
| Group | Patients | Progressed | Died of another cause | Alive and progression-free |
|---|---|---|---|---|
| Age < 70 | 574 | 62 | 240 | 272 |
| Age >= 70 | 810 | 53 | 620 | 137 |
figures/scripts/B3-05-competing-risk.RThe two tests are not asking the same thing
Gray’s test is the two-sample test for a CIF: it compares subdistribution hazards, which is to say it compares the two curves the reader is actually looking at. The log-rank test compares cause-specific hazards — and to do that it removes the competing event from the risk set as censoring, which is the standard way of getting at a cause-specific hazard in the first place (the “ordinary Cox model with competing events treated as censored” in the next section of this page does exactly the same thing).
So this is not one valid test and one broken one. What the second section of this page objects to is a different move: reading the 1 − KM curve computed off that censored fit as a cumulative incidence. The censoring itself is fine; what is not fine is using the resulting survival curve to answer how many people end up having the event.
Same data, same event of interest, same split — two tests answering two questions:
| Test | The question it answers | What it compares | What happens to the competing event | p |
|---|---|---|---|---|
| Gray’s test | How many people end up having the event (burden, prognosis) | The two CIFs | Kept in the model as a second absorbing state | 0.022 |
| Cause-specific log-rank | The rate of the event among people who have not yet had one (mechanism) | The two cause-specific hazards | Removed from the risk set as censoring | 0.412 |
At α = 0.05 the two conclusions do not agree: Gray’s test detects a difference between the two CIFs, while the cause-specific log-rank does not detect one.
The CIF and 1 - KM rank the groups in opposite orders
Two different p-values have an explanation: the tests are not asking the same question. What is worth stopping for is something else — the same question, answered by two estimators that put the groups in opposite orders.
| Group | Timepoint | CIF, progression (correct) | 1 − KM (wrong) | Overstatement | Still at risk |
|---|---|---|---|---|---|
| Age < 70 | 60 months | 2.5% | 2.8% | 1.16× | 434 |
| Age < 70 | 120 months | 6.4% | 8.1% | 1.27× | 270 |
| Age < 70 | 180 months | 11.2% | 15.9% | 1.42× | 146 |
| Age < 70 | 240 months | 13.4% | 20.3% | 1.51× | 53 |
| Age >= 70 | 60 months | 4.1% | 5.3% | 1.30× | 440 |
| Age >= 70 | 120 months | 6.4% | 11.0% | 1.73× | 154 |
| Age >= 70 | 180 months | 6.9% | 13.3% | 1.93× | 32 |
| Age >= 70 | 240 months | 7.8% | 27.8% | 3.55× | 4 |
The reversal belongs to a timepoint, not to the curves as a whole. Read the table down the timepoints and it is plain:
- Month 60: both methods put the older group on top.
- Month 180: both methods put the younger group on top.
- Month 240: the correct CIF says Age < 70 is higher (13.4% versus 7.8%), while 1 − KM says Age >= 70 is higher (27.8% versus 20.3%).
So do not file this away as the two curves run in opposite directions throughout. The curves cross partway through follow-up; what reverses is the ranking at month 240 — which happens to be exactly the timepoint many papers pick when they report a 20-year cumulative incidence.
The reason is lying in the same dataset. By month 240 the CIF for death from other causes is 51.7% in Age < 70 against 87.6% in Age >= 70, Gray’s test p < 0.001. The older group is not less prone to progression; most of them died of something else first. 1 − KM books those patients as still waiting to progress, and so inflates the older group past the younger one.
That steep jump in the tail was drawn by single digits
This page has already said that the late jumps in a 1 − KM curve happen because only single digits remain at risk. Split by group, that sentence acquires a number: Age >= 70 has 4 people left at risk by month 240 (Age < 70 has 53).
The near-vertical jump in the red dashed curve near the right edge of the figure is one of those few people progressing: the curve goes from 13.3% at month 180 to 27.8% at month 240, and the overstatement factor goes from 1.93× to 3.55×. When the denominator is down to single digits, one event moves the curve by more than ten percentage points.
This is the same point as item 5 of How to read this in a paper and the last few rows of Common misuses below: without a numbers-at-risk table you cannot tell how many people drew the tail. Under competing risks it carries one extra consequence — the curve that gets pushed up is the very one whose ranking reverses.
Stacked CIFs: three states filling up to 100%
In the section on why 1 − KM overstates, this page argued in prose that progression, death from other causes and being alive and progression-free add up to exactly 100%: a property that follows from the definition of the CIF and that 1 − KM does not have. That argument had no picture. Here it is.
figures/scripts/B3-05-competing-risk.RThis is not a schematic. The script checks that the three states sum to 1 at 538 actual transition times, and the largest deviation it finds is 1.1e-15 — floating-point rounding, not a number massaged into place. (The check runs on the real transition times, not on the 21 grid points exported for this figure; checking only the grid would be the easier test, since nothing can go wrong between two transitions.)
With the two panels side by side, the reversal from the previous section needs no further explanation:
- In Age >= 70, the brown band has taken 87.6% by month 240 and only 4.6% of the group is still alive and progression-free. The dark blue band is not being held down by some force — there is barely anyone left who could progress.
- In Age < 70, green still holds 35.0% at month 240. Blue therefore has room to keep climbing, and it reaches 13.4%.
How much green is left decides how high blue can climb. That is the whole mechanism of competing risks, and drawn this way it needs no formula.
It also makes this the fastest way to tell the two methods apart: what fills to 1 is a CIF; what fails to fill, or overflows, is 1 − KM. The callout earlier on this page — that 1 − KM curves for the various outcomes can sum to more than 100% — is the same statement, and item 2 of How to read this in a paper is this figure turned into a checking procedure.
Two hazards, two different questions
This is where competing risks is most often misused, and it is the real point of this page. There are two models, and they are not a right one and a wrong one — they answer different questions.
Cause-specific hazard
The condition in the denominator is : people who have had no event yet. Fitting it is nothing more than an ordinary Cox model with competing events treated as censored.
It answers: among people who have not yet had an event, how does this variable affect the rate of the event of interest? That is a question about biological mechanism — is this factor actually driving disease progression.
Subdistribution hazard (Fine-Gray)
The denominator has an extra piece: people who have already had a competing event stay in the risk set (technically, they are retained with a gradually decreasing weight). That sounds bizarre — how can someone already dead still be at risk? — but the purpose is precise: it makes the model’s coefficients map directly onto the height of the CIF. A Fine-Gray subdistribution hazard ratio above 1 means that variable pushes the CIF curve higher.
It answers: how does this variable affect how many people end up actually having the event? That is a question about burden and prognosis — whether to follow this group, for how long, and how to allocate resources.
In the MGUS data, the two give opposite answers
figures/scripts/B3-05-competing-risk.R| Variable | Cause-specific HR (95% CI) | Fine-Gray sHR (95% CI) |
|---|---|---|
| Age, per year | 1.016 (1.000–1.033) | 0.983 (0.973–0.994) |
| Male vs female | 0.995 (0.688–1.440) | 0.808 (0.568–1.150) |
| M-spike, per g/dL | 2.421 (1.751–3.346) | 2.431 (1.817–3.253) |
The two estimates for age point in opposite directions, and both of them are correct.
- Cause-specific HR = 1.016 (1.000–1.033, p = 0.051): among people who have not yet had an event, older patients progress at a slightly higher rate — though the interval sits right against 1, so this study did not detect a statistically significant difference.
- Fine-Gray sHR = 0.983 (0.973–0.994, p = 0.003): the older the patient, the lower the proportion who end up progressing, with the interval entirely to the left of 1.
Contradictory? Not at all. A third model settles it — same data, same covariates, but with death from other causes as the outcome:
| Variable | Cause-specific HR for death from other causes (95% CI) | p |
|---|---|---|
| Age, per year | 1.067 (1.060–1.075) | < 0.001 |
| Male vs female | 1.475 (1.286–1.692) | < 0.001 |
| M-spike, per g/dL | 0.942 (0.832–1.068) | 0.353 |
The hazard ratio for age on death from other causes is 1.067 (p < 0.001) — each additional year of age raises the rate of death from other causes by about 6.7%. Older patients are not less prone to progression; they die before they get there. At the biological level age does nothing protective for progression (if anything it points the other way), but at the level of “how many people actually progress”, age genuinely holds the CIF down.
By contrast, the two estimates for M-spike are nearly identical (2.421 versus 2.431), because M-spike has almost no effect on death from other causes (HR 0.942, p = 0.353; no association detected). The gap between the two kinds of hazard ratio comes from how strongly the variable acts on the competing event.
Run it yourself
library(survival)
data(cancer, package = "survival") # mgus2 ships in this data bundle
# 1. Collapse the two time axes into one, make the outcome a factor.
# The first level must be "censored" -- that is how survival reads a
# factor endpoint.
mg <- mgus2
mg$etime <- with(mg, ifelse(pstat == 0, futime, ptime))
mg$event <- with(mg, ifelse(pstat == 0, 2 * death, 1))
mg$event <- factor(mg$event, 0:2, c("censored", "progression", "death"))
table(mg$event)
# 2. CIF: give Surv() a factor and it switches to multi-state (Aalen-Johansen)
cif <- survfit(Surv(etime, event) ~ 1, data = mg)
summary(cif, times = c(60, 120, 240, 360)) # the pstate columns are the CIFs
plot(cif, col = c("#4d6a8c", "#8c6d4a"), lwd = 2, noplot = "(s0)",
xlab = "Months", ylab = "Cumulative incidence")
# 3. The wrong approach, for comparison: death censored, then 1 - KM off that
# fit read as a cumulative incidence
naive <- survfit(Surv(etime, pstat) ~ 1, data = mg)
1 - summary(naive, times = 240)$surv # clearly above the CIF above
# 4. Cause-specific Cox: censor the competing event, then it is ordinary coxph
coxph(Surv(etime, event == "progression") ~ age + sex + mspike, data = mg)
coxph(Surv(etime, event == "death") ~ age + sex + mspike, data = mg)
# 5. Fine-Gray: finegray() builds the weighted long table, then a weighted Cox
fgd <- finegray(Surv(etime, event) ~ ., data = mg, etype = "progression")
coxph(Surv(fgstart, fgstop, fgstatus) ~ age + sex + mspike,
weight = fgwt, data = fgd)
# -- Comparing two groups ---------------------------------------------------
mg$agegrp <- factor(ifelse(mg$age >= 70, "Age >= 70", "Age < 70"),
levels = c("Age < 70", "Age >= 70"))
mg$ecode <- as.integer(mg$event) - 1L # 0 censored, 1 progression, 2 death
# 6. Gray's test: the two-sample test for a CIF. It lives in cmprsk, not in
# survival (survival ships finegray() for the Fine-Gray model, but no
# Gray's test). Called with :: rather than library(cmprsk): attaching the
# package would put it ahead of survival on the search path, and a masked
# function could quietly change the three models above, which are already
# published on this page. A namespace-qualified call cannot mask anything.
gray <- cmprsk::cuminc(ftime = mg$etime, fstatus = mg$ecode,
group = as.character(mg$agegrp), cencode = 0)
gray$Tests # one row per cause: stat / pv / df
# cmprsk computes pv as 1 - pchisq(stat, df), which underflows to exactly 0
# once the statistic is large. A page printing "p = 0" is wrong in a way the
# reader cannot see, so recompute on the upper tail.
pchisq(gray$Tests[, "stat"], gray$Tests[, "df"], lower.tail = FALSE)
# 7. The cause-specific comparison: censor the competing deaths and run an
# ordinary log-rank. Censoring the competing event is the standard route to
# a cause-specific hazard, so this is a valid test -- it just asks the
# mechanism question, not the CIF question step 6 asks.
survdiff(Surv(etime, event == "progression") ~ agegrp, data = mg)
# 8. The grouped CIF and the stacked figure come from one object. With a factor
# endpoint survfit() returns pstate: one column per state, and every row sums
# to exactly 1 -- which is the property the stacked plot draws.
cif_g <- survfit(Surv(etime, event) ~ agegrp, data = mg)
sg <- summary(cif_g, times = c(60, 120, 180, 240))
sg$pstate # (s0) / progression / death
rowSums(sg$pstate) # every row is 1Verified with R 4.6.0 and survival 3.8.6. survival 3.x ships finegray(), so the Fine-Gray half needs no cmprsk; Gray's test is not in survival, though, and step 6 still needs it.
import pandas as pd
from lifelines import AalenJohansenFitter, KaplanMeierFitter
# statsmodels' get_rdataset cannot find it -- Rdatasets files it under
# another bundle -- so read the CSV directly.
RD = "https://vincentarelbundock.github.io/Rdatasets/csv/"
mg = pd.read_csv(RD + "survival/mgus2.csv")
mg["etime"] = mg["futime"].where(mg["pstat"] == 0, mg["ptime"])
mg["event"] = (2 * mg["death"]).where(mg["pstat"] == 0, 1) # 0 censor,1 prog,2 death
ajf = AalenJohansenFitter()
ajf.fit(mg["etime"], mg["event"], event_of_interest=1)
print(ajf.cumulative_density_.loc[:240].tail(1)) # CIF at 240 months
# The wrong approach, for comparison
kmf = KaplanMeierFitter().fit(mg["etime"], (mg["event"] == 1).astype(int))
print(1 - kmf.predict(240))
# Fine-Gray has no mature Python implementation; for a sHR, go back to
# survival::finegray() in R
# CIF by group: fit an AalenJohansenFitter within each group
mg["agegrp"] = (mg["age"] >= 70).map({True: "Age >= 70", False: "Age < 70"})
for g, sub in mg.groupby("agegrp"):
ajf = AalenJohansenFitter().fit(sub["etime"], sub["event"], event_of_interest=1)
print(g, float(ajf.cumulative_density_.loc[:240].iloc[-1]))
# The group test is missing too. lifelines ships statistics.logrank_test, but
# that is the cause-specific comparison -- it censors the competing event --
# not Gray's test. Python has nothing as mature as cmprsk::cuminc() for
# comparing CIFs, so for the p-value, go back to R.lifelines provides AalenJohansenFitter for the CIF. Fine-Gray has no implementation in Python as mature as R's, so in practice this work stays in R.
How to read this in a paper
When a paper involves competing risks — geriatric oncology, transplantation, device registries, dementia cohorts all qualify — work through this in order:
- Does it acknowledge that competing events exist? A primary outcome of “disease progression”, “revision surgery” or a specific cause of death in an elderly population, reported with nothing but a 1 − KM curve and a log-rank p-value, has done only the mechanism half: the cumulative incidence is overstated, and how many people end up having the event was never tested at all.
- Is the cumulative incidence a CIF or 1 − KM? A y-axis labelled “cumulative incidence” is no guarantee — some papers label 1 − KM that way too. The tell: if the curves for the various outcomes visibly sum to more than 1 at the end, it is 1 − KM.
- Which hazard ratio is it, and does the paper say? “HR” usually means cause-specific; “sHR” or “subdistribution hazard ratio” means Fine-Gray. If neither is stated, the only clue is the function named in the Methods (
coxphversuscrrorfinegray). - Does the interpretation match the model? Using a Fine-Gray sHR to argue that “this factor promotes disease progression” is a mismatch — the sHR speaks about final proportions, not mechanism.
- The numbers at risk. As important as in a KM plot, and for the same reason: you need to know how many people drew the tail of the curve.
Common misuses
| Misuse | Why it is wrong |
|---|---|
| Reporting cumulative incidence as 1 − KM when competing events exist | It credits the dead with hypothetical progressions; the overstatement grows with more competing events and longer follow-up |
| Not noticing that the 1 − KM curves sum to more than 100% | That is direct evidence that competing events were treated as censoring |
| Running KM with competing events as ordinary censoring and calling it “censoring handled by KM” | Censoring means “we stopped seeing them”; a competing event means “it can never happen now” — different things |
| Using a Fine-Gray sHR to argue about biological mechanism | The sHR maps onto the final cumulative proportion; mechanism questions need the cause-specific model |
| Using a cause-specific HR to state absolute risk or predict for an individual | It does not map onto the CIF, and a prediction model’s outputs must match observed proportions |
| Fitting both models and reporting only the significant one | Selective reporting; the two answer different questions, so the question decides which to report — or report both |
| Merging the competing event into a composite endpoint to sidestep the problem | What gets merged away is the clinical meaning; “progression or death” and “progression” are two different clinical questions |
| Competing risk analysis with no numbers-at-risk table | The same problem as in KM: the tail may rest on single digits |
| Reporting a non-significant cause-specific log-rank and concluding that the cumulative incidence is the same in both groups | The test and the claim sit at different levels: that sentence belongs to the CIF and needs Gray’s test or Fine-Gray — and reading Gray’s test as a statement about mechanism is the same mismatch in reverse |
| Writing a non-significant sHR up as “this factor is unrelated to cumulative incidence” | Say only that this study did not detect an association, and give the confidence interval |
| Fitting a multivariable Fine-Gray model on two or three events | With too few events, neither the coefficients nor the intervals are trustworthy |
Reproducing every number on this page
/opt/homebrew/bin/Rscript figures/scripts/B3-05-competing-risk.RRead the figure
The answer comes from the same statistical output that produced this page's figures, not from a number typed in beside them.
At 20 years, which number should be read as the cumulative incidence of progression to myeloma in these MGUS data?
Show the answer and why
Correct answer: 0.100 - from the CIF, in which death before progression removes the chance to progress
0.210 treats death from other causes as censoring, which assumes those patients would still have had a chance to progress had they lived; in a cohort with a median age in the seventies that plainly fails, and the estimate more than doubles as a result. 0.724 is the cumulative incidence of dying before progression. Note that the two CIFs do not add to the whole picture - the remainder is everyone who by 20 years had neither progressed nor died, and all three together make one. Adding the two CIFs and calling it complete is exactly how that group gets dropped.
Age points in opposite directions in the cause-specific Cox and Fine-Gray models. How should the subdistribution hazard ratio below one be read?
Show the answer and why
Correct answer: 0.983 - older patients more often die first, so the share who actually progress is lower
0.983 is the Fine-Gray estimate: older patients are likelier to die before progressing, so they spend less time in the group still able to progress and the observed share who do progress falls. 1.016 is the same term in the cause-specific model, and 1.067 is the cause-specific hazard ratio for age on death before progression. Neither model is wrong; they answer different questions - one asks whether age drives the biology of progression, the other asks what share of patients will actually progress. Clinical decisions usually want the second.
Of these 1384 patients, 115 progressed and 860 died before progressing. What should those 860 be in a survival analysis?
Show the answer and why
Correct answer: A competing event, leaving 409 genuinely censored
Censoring means the event we care about could still happen to this patient, and we merely stopped seeing it. Someone who has died cannot go on to progress, so calling them censored assumes something impossible - which is precisely what one minus KM does, and precisely why it overstates the risk. They are a competing event: they belong in the denominator and on the other curve. The genuinely censored are the 409 who were alive and progression-free when follow-up ended. 115 is the progression count, and folding deaths into it would be estimating a different outcome altogether.
Chapters that use this method
Watch next
COMPETING RISK EXPLAINED
Competing Risk Analysis
Easily Perform Competing Risks Survival Analysis with SAS StudioSources and licences
This page is original writing