AalenJohansen

Aalen-Johansen estimator of cumulative incidence functions for competing risks.

Usage

Source

AalenJohansen(
    *,
    conf_level=0.95,
)

In survival analysis, competing risks occur when subjects can experience multiple types of events (e.g., progression to malignancy vs. death from other causes), and experiencing one event precludes the others. The Aalen-Johansen estimator extends the Kaplan-Meier approach to this setting by estimating the cumulative incidence function (CIF) for each cause: the probability of experiencing that specific cause by time t, accounting for competition from other causes.

Unlike naive estimates that ignore censoring or competing events, the Aalen-Johansen CIF correctly accounts for both. It is computed using transition probabilities between states via generalized Kaplan-Meier estimates. Call fit() with a multi-state Surv response (built with Surv.multistate()) to obtain estimates for each competing cause. Results are returned as tidy DataFrames with one row per combination of stratum, cause, and time.

The estimator uses the formula \(\mathrm{CIF}_j(t) = \sum_{s \le t} \hat{S}(s^-) P_{0j}(s)\), where \(\hat{S}(s^-)\) is the probability of being event-free before time \(s\), and \(P_{0j}(s)\) is the transition probability from censoring to cause \(j\). Confidence intervals use the Greenwood-style variance estimator on the complementary log-log scale for improved coverage.

Parameters

conf_level: float = 0.95
Confidence level for the (Wald) confidence intervals (default 0.95).

Returns

Fitted estimator
Call fit() to produce a fitted estimator with cached results (states_, and internal transition matrices), accessible as tidy DataFrames.

Details

Call fit(surv, by=...) with a multi-state Surv response (built with Surv.multistate, where event codes are 0 for censoring and 1..K for the competing causes). Results are tidy frames via to_frame() (optionally format=) with one row per stratum, cause, and time.

Examples

The bundled mgus2 dataset follows monoclonal-gammopathy patients who may progress to plasma-cell malignancy ("pcm") or die first, a competing-risks setup. Build the competing-risks response by combining the progression and death indicators into a single cause code (0 censored, 1 progression, 2 death), then fit the estimator. Printing the fitted object reports the final cumulative incidence for each cause.

import greenwood as gw

mg = gw.load_dataset("mgus2", backend="pandas")
etime = mg["ptime"].where(mg["pstat"] == 1, mg["futime"])
cause = mg["pstat"].where(mg["pstat"] == 1, 2 * mg["death"])
cr = gw.Surv.multistate(etime, event=cause, states=("pcm", "death"))
aj = gw.AalenJohansen().fit(cr)
aj
AalenJohansen (Aalen-Johansen cumulative incidence)

states: pcm, death
n = 1384

       final CIF
pcm       0.1613
death     0.8387

Methods

Name Description
fit() Fit cumulative incidence functions to a competing-risks response.
to_frame() Return cumulative-incidence estimates as a DataFrame.

fit()

Fit cumulative incidence functions to a competing-risks response.

Usage

Source

fit(surv, *, by=None)

Computes the cumulative incidence function (CIF) for each cause-of-interest from a multi-state Surv response. Unlike Kaplan-Meier (which handles only a single event type), the Aalen-Johansen estimator accounts for competing events: subjects who experience a competing cause are removed from the risk set, preventing overly optimistic estimates of the probability of experiencing the target cause. Results are stored in the fitted object; access them via to_frame() (optionally format=).

The Aalen-Johansen estimator generalizes both Kaplan-Meier and Nelson-Aalen to the competing-risks setting. For each cause \(j\), it estimates \(F_j(t)\), the cumulative probability of experiencing cause \(j\) by time \(t\), accounting for all competing causes. The CIFs sum to the overall event probability at any time. Pass by= to produce separate CIF estimates per group (stratified competing-risks analysis).

Parameters
surv: Surv

A multi-state Surv response built with Surv.multistate(). Must have multiple causes-of-interest. Raises ValueError if a single-event response is passed (use KaplanMeier for that).

by: Any = None
Optional grouping variable (e.g., a column or array). Produces one set of cumulative incidence functions per unique value of by. Default (None): fit a single, unstratified set of CIFs.
Returns
AalenJohansen
The fitted estimator object itself (for method chaining) with cached cumulative incidence results (time arrays, CIF per cause, confidence bands) accessible via to_frame() (optionally format=).
Details

The Aalen-Johansen estimator is a product-integral estimator of the CIF: \(F_j(t) = \int \hat{S}_{-}(u) \, dM_j(u)\), where \(\hat{S}_{-}(u)\) is the estimated probability of surviving (remaining uncensored) just before \(u\), and \(M_j(u)\) is the counting process for cause \(j\). It reduces to Kaplan-Meier when there is only one cause and no censoring.

Left truncation is not yet supported. Multi-state responses must be built with Surv.multistate().

Examples

Fit cumulative incidence functions on the competing-risks mgus2 dataset, where subjects can experience plasma-cell malignancy (pcm) or death:

import greenwood as gw

mg = gw.load_dataset("mgus2", backend="pandas")
etime = mg["ptime"].where(mg["pstat"] == 1, mg["futime"])
cause = mg["pstat"].where(mg["pstat"] == 1, 2 * mg["death"])
cr = gw.Surv.multistate(etime, event=cause, states=("pcm", "death"))
aj = gw.AalenJohansen().fit(cr)
aj
AalenJohansen (Aalen-Johansen cumulative incidence)

states: pcm, death
n = 1384

       final CIF
pcm       0.1613
death     0.8387

Fit stratified cumulative incidence functions by sex to compare risk accumulation:

aj_stratified = gw.AalenJohansen().fit(cr, by=mg["sex"])
aj_stratified
AalenJohansen (Aalen-Johansen cumulative incidence)

states: pcm, death
strata: 2

to_frame()

Return cumulative-incidence estimates as a DataFrame.

Usage

Source

to_frame(*, format=None)

Exports the Aalen-Johansen fit with one row per cause and time point, including the risk set, cumulative-incidence estimate, standard error, confidence limits, and optional strata labels.

Parameters
format: str | None = None
Output format: None (default), "pandas", "polars", or "pyarrow". When None, a backend is auto-detected (Polars, then Pandas, then PyArrow).
Returns
pandas.DataFrame, polars.DataFrame, or pyarrow.Table
A tidy table with columns cause, time, n_risk, estimate, std_error, conf_low, conf_high, and optionally strata.
Raises
ImportError
If the requested (or, when auto-detecting, any) DataFrame library is not installed.
Examples

Fit the estimator on the competing-risks mgus2 data and export the cumulative- incidence functions as a Polars frame:

import greenwood as gw

mg = gw.load_dataset("mgus2", backend="pandas")
etime = mg["ptime"].where(mg["pstat"] == 1, mg["futime"])
cause = mg["pstat"].where(mg["pstat"] == 1, 2 * mg["death"])
cr = gw.Surv.multistate(etime, event=cause, states=("pcm", "death"))
aj = gw.AalenJohansen().fit(cr)
aj.to_frame(format="polars")
shape: (536, 7)
causetimen_riskestimatestd_errorconf_lowconf_high
strf64f64f64f64f64f64
"pcm"1.01384.00.00.00.00.0
"pcm"2.01341.00.0014460.0010220.00.003449
"pcm"3.01311.00.0014460.0010220.00.003449
"pcm"4.01296.00.0021690.0012510.00.004621
"pcm"5.01279.00.0028920.0014440.0000620.005723
"death"341.05.00.7842080.0209330.7431790.825237
"death"350.04.00.7842080.0209330.7431790.825237
"death"373.03.00.7842080.0209330.7431790.825237
"death"394.02.00.7842080.0209330.7431790.825237
"death"424.01.00.8387080.0282880.7832650.894152

Request a different backend with format=:

aj.to_frame(format="pandas")
cause time n_risk estimate std_error conf_low conf_high
0 pcm 1.0 1384.0 0.000000 0.000000 0.000000 0.000000
1 pcm 2.0 1341.0 0.001446 0.001022 0.000000 0.003449
2 pcm 3.0 1311.0 0.001446 0.001022 0.000000 0.003449
3 pcm 4.0 1296.0 0.002169 0.001251 0.000000 0.004621
4 pcm 5.0 1279.0 0.002892 0.001444 0.000062 0.005723
... ... ... ... ... ... ... ...
531 death 341.0 5.0 0.784208 0.020933 0.743179 0.825237
532 death 350.0 4.0 0.784208 0.020933 0.743179 0.825237
533 death 373.0 3.0 0.784208 0.020933 0.743179 0.825237
534 death 394.0 2.0 0.784208 0.020933 0.743179 0.825237
535 death 424.0 1.0 0.838708 0.028288 0.783265 0.894152

536 rows × 7 columns