NelsonAalen

Nelson-Aalen estimator of the cumulative hazard.

Usage

Source

NelsonAalen(
    *,
    conf_type="log",
    conf_level=0.95,
)

The Nelson-Aalen estimator provides a non-parametric estimate of the cumulative hazard function, which represents the total “accumulated risk” up to a given time. Unlike the Kaplan-Meier estimator which models survival directly, this approach models the force of mortality. The cumulative hazard at each event time is computed as a running sum of the ratio of events to subjects at risk:

\[ H(t) = \sum_{t_i \le t} \frac{d_i}{n_i} \]

This estimator is useful when you want to examine the hazard directly rather than survival probabilities, and is often used as the basis for other analyses. You can convert the cumulative hazard to a survival estimate via \(S(t) = \exp(-H(t))\), though the Kaplan-Meier estimator is typically preferred for direct survival estimation. Call fit() with a right-censored Surv response to compute cumulative hazard at each event time.

The variance of the cumulative hazard estimate uses Aalen’s formula:

\[ \mathrm{Var}(H(t)) = \sum_{t_i \le t} \frac{d_i}{n_i^2} \]

Confidence intervals can be constructed on the plain or log scale, with the log scale providing better coverage in the tails.

Parameters

conf_type: str = "log"

Confidence-interval transform: "plain" (default for Nelson-Aalen) or "log".

conf_level: float = 0.95
Confidence level for the interval (default 0.95).

Returns

Fitted estimator
Call fit() to produce a fitted estimator with cached results (time_, cumulative_hazard_, std_error_, conf_low_, conf_high_, n_risk_, n_event_, n_censor_), accessible as aligned arrays or exported to DataFrames.

Details

Call fit() with a Surv response. Results are exposed as aligned arrays, as tidy frames via to_frame() (optionally format=), and through the predict(), quantile(), and other methods.

Examples

Build a Surv response from the bundled lung dataset and fit the estimator. Printing the fitted object reports the counts and the maximum cumulative hazard reached.

import greenwood as gw

lung = gw.load_dataset("lung", backend="polars")
y = gw.Surv.right(lung["time"], event=(lung["status"] == 2))
na = gw.NelsonAalen().fit(y)
na
NelsonAalen (Nelson-Aalen cumulative hazard estimate)

    n  events  max cumhaz
  228     165       2.889

Methods

Name Description
fit() Fit the Nelson-Aalen estimator to survival data.
to_frame() Return the fitted cumulative hazard as a DataFrame.

fit()

Fit the Nelson-Aalen estimator to survival data.

Usage

Source

fit(surv, *, by=None, weights=None)

Computes the cumulative hazard function \(H(t)\) from a Surv response (time-to-event data). Like Kaplan-Meier, this is a non-parametric estimate requiring no distributional assumptions. The Nelson-Aalen estimator is an alternative to Kaplan-Meier; it estimates the cumulative hazard directly (sum of \(d/n\) at each event time), from which the survival probability can be derived via \(S(t) = \exp(-H(t))\). Results are stored in the fitted object; access them via attributes or export to a DataFrame with to_frame() (optionally format=).

Pass by= to produce separate cumulative hazard curves per group (stratified analysis), enabling covariate-free comparison of hazard accumulation across groups. Optionally supply weights to adjust for selection bias or survey design.

Parameters
surv: Surv

A Surv response (typically right-censored). Built from data using Surv.right(), Surv.interval(), etc.

by: Any = None

Optional grouping variable (e.g., a column or array). Produces one fit (one cumulative hazard curve) per unique value of by, enabling stratified Nelson-Aalen analysis. Default (None): fit a single, unstratified curve.

weights: Any = None
Optional weights (e.g., from survey design or inverse-probability-of-censoring adjustments). Must have the same length as surv. Default (None): unit weights.
Returns
NelsonAalen
The fitted estimator object itself (for method chaining) with cached results (time_, cumulative_hazard_, conf_low_, conf_high_, n_risk_, n_event_ as attributes).
Details

The Nelson-Aalen estimator is

\[ H(t) = \sum_{t_i \le t} \frac{d_i}{n_i} \]

where \(d_i\) and \(n_i\) are events and number at risk at time \(t_i\). Its variance is estimated using Aalen’s formula:

\[ \mathrm{Var}(H) = \sum \frac{d_i}{n_i^2} \]

The survival function can be recovered as \(S(t) = \exp(-H(t))\). Confidence intervals are point-wise.

Examples

Fit a single (unstratified) cumulative hazard curve on the bundled lung dataset:

import greenwood as gw

lung = gw.load_dataset("lung", backend="polars")
y = gw.Surv.right(lung["time"], event=(lung["status"] == 2))
na = gw.NelsonAalen().fit(y)
na
NelsonAalen (Nelson-Aalen cumulative hazard estimate)

    n  events  max cumhaz
  228     165       2.889

Fit stratified curves by sex to compare cumulative hazard accumulation:

na_stratified = gw.NelsonAalen().fit(y, by=lung["sex"])
na_stratified
NelsonAalen (Nelson-Aalen cumulative hazard estimate)

     n  events  max cumhaz
1  138     112       3.163
2   90      53       2.322

to_frame()

Return the fitted cumulative hazard as a DataFrame.

Usage

Source

to_frame(*, format=None)

Exports the Nelson-Aalen estimate with one row per event time, including risk-set counts, the cumulative hazard estimate, its 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 time, n_risk, n_event, 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 a Nelson-Aalen estimator on the bundled lung dataset, then export the fitted cumulative-hazard curve as a Polars frame:

import greenwood as gw

lung = gw.load_dataset("lung", backend="polars")
y = gw.Surv.right(lung["time"], event=(lung["status"] == 2))
na = gw.NelsonAalen().fit(y)
na.to_frame(format="polars")
shape: (186, 7)
timen_riskn_eventestimatestd_errorconf_lowconf_high
f64f64f64f64f64f64f64
5.0228.01.00.0043860.0043860.0006180.031136
11.0227.03.00.0176020.0088010.0066060.046899
12.0224.01.00.0220660.0098680.0091840.053015
13.0223.02.00.0310350.011730.0147950.065101
15.0221.01.00.035560.0125730.0177830.071108
840.05.00.02.6392670.3358592.0566673.386903
883.04.01.02.8892670.418692.1748953.838285
965.03.00.02.8892670.418692.1748953.838285
1010.02.00.02.8892670.418692.1748953.838285
1022.01.00.02.8892670.418692.1748953.838285

Pass a different format= for pandas or PyArrow output:

na.to_frame(format="pandas")
time n_risk n_event estimate std_error conf_low conf_high
0 5.0 228.0 1.0 0.004386 0.004386 0.000618 0.031136
1 11.0 227.0 3.0 0.017602 0.008801 0.006606 0.046899
2 12.0 224.0 1.0 0.022066 0.009868 0.009184 0.053015
3 13.0 223.0 2.0 0.031035 0.011730 0.014795 0.065101
4 15.0 221.0 1.0 0.035560 0.012573 0.017783 0.071108
... ... ... ... ... ... ... ...
181 840.0 5.0 0.0 2.639267 0.335859 2.056667 3.386903
182 883.0 4.0 1.0 2.889267 0.418690 2.174895 3.838285
183 965.0 3.0 0.0 2.889267 0.418690 2.174895 3.838285
184 1010.0 2.0 0.0 2.889267 0.418690 2.174895 3.838285
185 1022.0 1.0 0.0 2.889267 0.418690 2.174895 3.838285

186 rows × 7 columns