calibration()

Assess calibration of predicted survival probabilities at a fixed time.

Usage

Source

calibration(
    surv,
    predicted,
    time,
    *,
    n_bins=10,
    conf_level=0.95,
    format=None,
)

Subjects are grouped into n_bins bins by their predicted survival probability at time. Within each bin the mean prediction is compared against the observed survival, a Kaplan-Meier estimate at time for that bin’s subjects. A well-calibrated model has the observed values close to the predicted ones (points near the diagonal).

Parameters

surv: Surv

The Surv response (right-censored or counting-process).

predicted: Any

Predicted survival probability at time, one per subject (for example a column of CoxPH.predict(newdata, type="survival", times=[time])).

time: float

The horizon at which predictions are assessed.

n_bins: int = 10

Number of prediction bins (default 10). Bins are quantile-based. Empty bins are dropped, so ties in predicted may yield fewer rows.

conf_level: float = 0.95

Confidence level for the observed (Kaplan-Meier) interval.

format: str | None = None
Output format for the returned DataFrame: None (default), "pandas", "polars", or "pyarrow". None (the default) will auto-detects and prefer Polars if available (falls back to Pandas, then Pyarrow, and raises an error if no DataFrame library is available).

Returns

DataFrame
One row per bin with columns bin, n, predicted (mean), observed, observed_lower, observed_upper. Format depends on the format parameter.

Details

Bins are formed by splitting subjects into n_bins quantile-based groups of their predicted survival probability. Within each bin the Kaplan–Meier estimate at time gives the observed survival, and the mean of the predictions gives the predicted value. A well-calibrated model produces points that lie along the 45-degree diagonal when plotted as observed vs. predicted. Systematic departures from the diagonal indicate over- or under-prediction in that probability range.

Examples

Fit a Cox model on the bundled lung dataset and assess calibration at one year. Each bin’s mean predicted survival is compared against the observed Kaplan–Meier survival for that bin’s subjects:

import greenwood as gw

# Load data and build a right-censored response
lung = gw.load_dataset("lung", backend="polars")
y = gw.Surv.right(lung["time"], event=(lung["status"] == 2))
cox = gw.CoxPH().fit(y, lung[["age", "sex"]])

# Assess one-year calibration across five prediction bins
surv = cox.predict(lung[["age", "sex"]], type="survival", times=[365.0], format="pandas")
predicted = surv.iloc[0, 1:].to_numpy()
gw.calibration(y, predicted, 365.0, n_bins=5, format="polars")
shape: (5, 6)
binnpredictedobservedobserved_lowerobserved_upper
i64i64f64f64f64f64
1450.2756440.2265150.125630.408414
2440.3259930.3630820.2378040.554357
3450.3886240.4081160.2844550.585537
4450.4853780.5400040.3954970.73731
5490.5686380.5142850.3762780.702909