Integrated (time-averaged) Brier score across multiple time points.
integrated_brier_score(
surv,
survival_prob,
times,
)
Summarizes Brier scores computed at multiple time horizons into a single summary metric via trapezoidal integration. This provides an overall calibration measure that doesn’t emphasize any particular time point.
Use this to: Reduce multiple Brier scores (one per time point) to a single number for model comparison. A single IBS score makes it easier to compare two models or report a single “calibration quality” metric.
Interpretation: Same scale as Brier score (0 = perfect, 1 = worst). Values of 0.15-0.25 are typical for reasonable survival models; values > 0.30 suggest poor calibration.
Parameters
surv: Surv
-
A right-censored Surv response.
survival_prob: Any
-
Predicted survival probabilities, shape (n_subjects, n_times).
times: Any
-
Evaluation times (must be at least 2 to define an interval). The IBS is computed as the area under the Brier-score curve from times[0] to times[-1], normalized by the time span.
Returns
float
-
Integrated Brier score (time-averaged). Lower is better.
Details
Computation: The integrated Brier score is
\[
IBS = \frac{1}{t_{\max} - t_{\min}} \int BS(t) \, dt
\]
Using trapezoidal rule to approximate the integral. This ensures the summary score balances contributions from all times without emphasizing early or late horizons.
Time scale sensitivity: IBS weights contribution proportionally to time intervals. If you have many time points clustered early (e.g., 10 times in [0, 100] and 1 time at [1000]), early times dominate. Use evenly-spaced time points for balanced assessment.
Examples
Fit a Cox model and compute its integrated Brier score over a range of times:
import greenwood as gw
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"]])
times = [180, 365, 540]
surv_pred = cox.predict(lung[["age", "sex"]], type="survival", times=times, format="pandas")
probs = surv_pred.iloc[:, 1:].to_numpy().T
ibs = gw.integrated_brier_score(y, probs, times)
ibs
Compare two models via their integrated Brier scores. Lower is better:
# cox2 = CoxPH().fit(y, lung[["age", "sex", "ph.ecog"]]) # More covariates
# surv_pred2 = cox2.predict(...)
# ibs2 = gw.integrated_brier_score(y, probs2, times)
# print(f"Model 1 IBS: {ibs:.3f}")
# print(f"Model 2 IBS: {ibs2:.3f}")
# print(f"Better model: {'Model 2' if ibs2 < ibs else 'Model 1'}")
Compute integrated Brier score over a wide range of times to get an overall calibration assessment:
times_wide = list(range(100, 700, 50))
surv_pred_wide = cox.predict(
lung[["age", "sex"]], type="survival", times=times_wide, format="pandas"
)
probs_wide = surv_pred_wide.iloc[:, 1:].to_numpy().T
ibs_wide = gw.integrated_brier_score(y, probs_wide, times_wide)
print(f"IBS over {len(times_wide)} time points: {ibs_wide:.3f}")
IBS over 12 time points: 0.200