Skill Tracer
Skills › Scientific Agent Skills › scikit-survival › cox-models.md

cox-models.md

259 lines · skills/scikit-survival/references/cox-models.md at 5f2bc03

Open on GitHub
1# Cox, Coxnet, and IPC ridge models
2
3Verified for scikit-survival 0.28.0 on 2026-10-01.
4

Cox, Coxnet, and IPC ridge models

Verified for scikit-survival 0.28.0 on 2026-10-01.

Raw lines 1 to 4
5## Cox proportional hazards model
6
7For covariates \(x\),
8
9\[
10h(t \mid x) = h_0(t)\exp(x^\top\beta).
11\]
13`CoxPHSurvivalAnalysis` estimates coefficients by partial likelihood. The model
14assumes covariate effects multiply the hazard by a time-constant factor. A fitted
15coefficient is a log hazard ratio only under the model, coding, scale, and PH
16assumptions.
18```python
19from sksurv.linear_model import CoxPHSurvivalAnalysis
21model = CoxPHSurvivalAnalysis(
22 alpha=0.1,
23 ties="efron",
24 n_iter=100,
25 tol=1e-9,
27model.fit(X_train, y_train)
28risk = model.predict(X_test)
29survival = model.predict_survival_function(X_test)
30hazard = model.predict_cumulative_hazard_function(X_test)
31```
33Current key parameters:
35- `alpha`: non-negative L2/ridge penalty. It may be a scalar or feature-specific
36 vector where documented. `alpha=0` is unpenalized.
37- `ties`: `"breslow"` (default) or `"efron"`.
38- `n_iter`, `tol`, `verbose`: Newton-Raphson controls.
40`predict()` returns the linear predictor \(x^\top\hat\beta\); higher means higher
41event risk. Absolute survival probabilities come from the fitted baseline survival,
42not from transforming a risk score by itself.

Cox proportional hazards model

For covariates \(x\),

\[ h(t \mid x) = h_0(t)\exp(x^\top\beta). \]

CoxPHSurvivalAnalysis estimates coefficients by partial likelihood. The model assumes covariate effects multiply the hazard by a time-constant factor. A fitted coefficient is a log hazard ratio only under the model, coding, scale, and PH assumptions.

from sksurv.linear_model import CoxPHSurvivalAnalysis

model = CoxPHSurvivalAnalysis(
    alpha=0.1,
    ties="efron",
    n_iter=100,
    tol=1e-9,
)
model.fit(X_train, y_train)
risk = model.predict(X_test)
survival = model.predict_survival_function(X_test)
hazard = model.predict_cumulative_hazard_function(X_test)

Current key parameters:

  • alpha: non-negative L2/ridge penalty. It may be a scalar or feature-specific vector where documented. alpha=0 is unpenalized.
  • ties: "breslow" (default) or "efron".
  • n_iter, tol, verbose: Newton-Raphson controls.

predict() returns the linear predictor \(x^\top\hat\beta\); higher means higher event risk. Absolute survival probabilities come from the fitted baseline survival, not from transforming a risk score by itself.

Raw lines 5 to 43
44### Stability and interpretation
46- Encode and scale inside a training-fitted pipeline.
47- Use ridge shrinkage for unstable or correlated designs; a successful numerical
48 fit does not establish inferential validity.
49- Check coefficient sensitivity to coding, scaling, missingness, influential rows,
50 and regularization.
51- Exponentiating a coefficient gives a model-based hazard ratio for one unit of its
52 encoded feature, holding other modeled features fixed.
53- A hazard ratio is not a risk ratio, probability difference, causal effect, or
54 clinical utility measure.
56scikit-survival does not provide a complete PH-diagnostics workflow. Assess the PH
57assumption using residual/graphical/domain methods appropriate to the study. If it
58fails, consider time interactions, stratification in a method that supports it, a
59time-varying model, an AFT model, or a flexible prediction model. Do not merely
60switch models and retain Cox coefficient interpretation.

Stability and interpretation

  • Encode and scale inside a training-fitted pipeline.
  • Use ridge shrinkage for unstable or correlated designs; a successful numerical fit does not establish inferential validity.
  • Check coefficient sensitivity to coding, scaling, missingness, influential rows, and regularization.
  • Exponentiating a coefficient gives a model-based hazard ratio for one unit of its encoded feature, holding other modeled features fixed.
  • A hazard ratio is not a risk ratio, probability difference, causal effect, or clinical utility measure.

scikit-survival does not provide a complete PH-diagnostics workflow. Assess the PH assumption using residual/graphical/domain methods appropriate to the study. If it fails, consider time interactions, stratification in a method that supports it, a time-varying model, an AFT model, or a flexible prediction model. Do not merely switch models and retain Cox coefficient interpretation.

Raw lines 44 to 61
62## Penalized Cox path with Coxnet
64`CoxnetSurvivalAnalysis` implements a Cox elastic-net path:
66\[
67\text{penalty} =
68\alpha\left(\rho\|\beta\|_1 + \frac{1-\rho}{2}\|\beta\|_2^2\right),
69\]
71where `l1_ratio` is \(\rho\).
73```python
74from sksurv.linear_model import CoxnetSurvivalAnalysis
76model = CoxnetSurvivalAnalysis(
77 n_alphas=100,
78 alpha_min_ratio="auto",
79 l1_ratio=0.9,
80 fit_baseline_model=True,
82model.fit(X_train_scaled, y_train)
83```
85Current details:
87- `l1_ratio` must be in `(0, 1]`; `1.0` is LASSO and values below 1 mix L1/L2.
88 Exact pure ridge is handled by `CoxPHSurvivalAnalysis(alpha=...)`, not by setting
89 `l1_ratio=0`.
90- `alphas=None` estimates a decreasing path; explicit `alphas` selects the path.
91- `alpha_min_ratio` controls the smallest/largest path ratio. It is not the
92 L1/L2 mixing parameter.
93- `penalty_factor` can vary penalties by feature; zero leaves a feature unpenalized.
94- `normalize=False` is current. Prefer an explicit `StandardScaler` pipeline so
95 fold behavior and feature scaling are visible.
96- `coef_` has shape `(n_features, n_alphas)`. There is no current `coef_path_`
97 attribute.
98- `predict(X, alpha=...)` uses the selected path point (or interpolation).
99- `predict_survival_function()` and
100 `predict_cumulative_hazard_function()` require
101 `fit_baseline_model=True`. An explicit `alpha` for either probability method must
102 match a fitted `alphas_` value; risk-score interpolation does not provide an
103 interpolated baseline survival model.

Penalized Cox path with Coxnet

CoxnetSurvivalAnalysis implements a Cox elastic-net path:

\[ \text{penalty} = \alpha\left(\rho\|\beta\|_1 + \frac{1-\rho}{2}\|\beta\|_2^2\right), \]

where l1_ratio is \(\rho\).

from sksurv.linear_model import CoxnetSurvivalAnalysis

model = CoxnetSurvivalAnalysis(
    n_alphas=100,
    alpha_min_ratio="auto",
    l1_ratio=0.9,
    fit_baseline_model=True,
)
model.fit(X_train_scaled, y_train)

Current details:

  • l1_ratio must be in (0, 1]; 1.0 is LASSO and values below 1 mix L1/L2. Exact pure ridge is handled by CoxPHSurvivalAnalysis(alpha=...), not by setting l1_ratio=0.
  • alphas=None estimates a decreasing path; explicit alphas selects the path.
  • alpha_min_ratio controls the smallest/largest path ratio. It is not the L1/L2 mixing parameter.
  • penalty_factor can vary penalties by feature; zero leaves a feature unpenalized.
  • normalize=False is current. Prefer an explicit StandardScaler pipeline so fold behavior and feature scaling are visible.
  • coef_ has shape (n_features, n_alphas). There is no current coef_path_ attribute.
  • predict(X, alpha=...) uses the selected path point (or interpolation).
  • predict_survival_function() and predict_cumulative_hazard_function() require fit_baseline_model=True. An explicit alpha for either probability method must match a fitted alphas_ value; risk-score interpolation does not provide an interpolated baseline survival model.
Raw lines 62 to 104
105### Leakage-safe alpha selection
107Do not estimate the alpha path on all rows and then claim nested-CV performance.
108For a final held-out evaluation:
1101. split train/test;
1112. define an alpha grid from subject-matter scale or from training data only;
1123. fit scaler and Coxnet inside each inner fold;
1134. tune alpha on inner validation folds;
1145. evaluate the selected procedure in an outer fold or untouched test set.
116```python
117from sklearn.model_selection import GridSearchCV
118from sklearn.pipeline import make_pipeline
119from sklearn.preprocessing import StandardScaler
121pipeline = make_pipeline(
122 StandardScaler(),
123 CoxnetSurvivalAnalysis(l1_ratio=0.9, fit_baseline_model=True),
125search = GridSearchCV(
126 pipeline,
127 {
128 "coxnetsurvivalanalysis__alphas": [
129 [0.01],
130 [0.05],
131 [0.2],
132 ]
133 },
134 cv=inner_splits,
135 error_score="raise",
137search.fit(X_outer_train, y_outer_train)
138```
140When using censoring-aware scorer wrappers, wrap the entire pipeline and prefix
141the parameter again:
143```python
144from sksurv.metrics import as_concordance_index_ipcw_scorer
146wrapped = as_concordance_index_ipcw_scorer(pipeline, tau=tau)
147search = GridSearchCV(
148 wrapped,
149 {
150 "estimator__coxnetsurvivalanalysis__alphas": [
151 [0.01],
152 [0.05],
153 [0.2],
154 ]
155 },
156 cv=inner_splits,
158```
160IPCRidge `.score()` handles its time direction, but metric scorer wrappers call raw
161`.predict()` and do not automatically negate time-oriented predictions. Use an
162explicit risk adapter if applying a discrimination wrapper to an AFT estimator.
164The wrapper is the estimator passed to `GridSearchCV`; it is not a zero-argument
165`scoring` callable. Its fit fold supplies the censoring distribution. Time support
166still has to be valid in every score fold.

Leakage-safe alpha selection

Do not estimate the alpha path on all rows and then claim nested-CV performance. For a final held-out evaluation:

  1. split train/test;
  2. define an alpha grid from subject-matter scale or from training data only;
  3. fit scaler and Coxnet inside each inner fold;
  4. tune alpha on inner validation folds;
  5. evaluate the selected procedure in an outer fold or untouched test set.
from sklearn.model_selection import GridSearchCV
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler

pipeline = make_pipeline(
    StandardScaler(),
    CoxnetSurvivalAnalysis(l1_ratio=0.9, fit_baseline_model=True),
)
search = GridSearchCV(
    pipeline,
    {
        "coxnetsurvivalanalysis__alphas": [
            [0.01],
            [0.05],
            [0.2],
        ]
    },
    cv=inner_splits,
    error_score="raise",
)
search.fit(X_outer_train, y_outer_train)

When using censoring-aware scorer wrappers, wrap the entire pipeline and prefix the parameter again:

from sksurv.metrics import as_concordance_index_ipcw_scorer

wrapped = as_concordance_index_ipcw_scorer(pipeline, tau=tau)
search = GridSearchCV(
    wrapped,
    {
        "estimator__coxnetsurvivalanalysis__alphas": [
            [0.01],
            [0.05],
            [0.2],
        ]
    },
    cv=inner_splits,
)

IPCRidge .score() handles its time direction, but metric scorer wrappers call raw .predict() and do not automatically negate time-oriented predictions. Use an explicit risk adapter if applying a discrimination wrapper to an AFT estimator.

The wrapper is the estimator passed to GridSearchCV; it is not a zero-argument scoring callable. Its fit fold supplies the censoring distribution. Time support still has to be valid in every score fold.

Raw lines 105 to 167
168### Feature selection is uncertain
170Non-zero coefficients at one alpha do not prove that a feature is biologically,
171causally, or clinically important. Report:
173- the exact preprocessing and penalty grid;
174- nested-CV or holdout protocol;
175- coefficient stability across resamples;
176- correlated alternatives and selection frequency;
177- the selected alpha and `l1_ratio`;
178- calibration and discrimination on independent data.

Feature selection is uncertain

Non-zero coefficients at one alpha do not prove that a feature is biologically, causally, or clinically important. Report:

  • the exact preprocessing and penalty grid;
  • nested-CV or holdout protocol;
  • coefficient stability across resamples;
  • correlated alternatives and selection frequency;
  • the selected alpha and l1_ratio;
  • calibration and discrimination on independent data.
Raw lines 168 to 179
180## IPC ridge AFT model
182`IPCRidge` is an inverse-probability-of-censoring weighted ridge regression model
183for a log-time/AFT objective:
185```python
186from sksurv.linear_model import IPCRidge
188model = IPCRidge(alpha=1.0)
189model.fit(X_train_scaled, y_train)
190predicted_time = model.predict(X_test_scaled)
191```
193The fitted linear predictor models log time, but `predict()` exponentiates it and
194returns original-time values; do not exponentiate it again. This output is time-oriented: larger predicted values imply longer predicted
195survival time, unlike higher-is-riskier Cox scores. Do not pass it unchanged to
196metrics expecting higher event risk. If a discrimination analysis requires a
197risk direction, use the negative prediction and state that transformation.
199IPCW estimation relies on censoring assumptions and support. High censoring is not
200by itself a license to prefer the model; inspect weight stability and whether the
201training censoring distribution is positive over the target range.

IPC ridge AFT model

IPCRidge is an inverse-probability-of-censoring weighted ridge regression model for a log-time/AFT objective:

from sksurv.linear_model import IPCRidge

model = IPCRidge(alpha=1.0)
model.fit(X_train_scaled, y_train)
predicted_time = model.predict(X_test_scaled)

The fitted linear predictor models log time, but predict() exponentiates it and returns original-time values; do not exponentiate it again. This output is time-oriented: larger predicted values imply longer predicted survival time, unlike higher-is-riskier Cox scores. Do not pass it unchanged to metrics expecting higher event risk. If a discrimination analysis requires a risk direction, use the negative prediction and state that transformation.

IPCW estimation relies on censoring assumptions and support. High censoring is not by itself a license to prefer the model; inspect weight stability and whether the training censoring distribution is positive over the target range.

Raw lines 180 to 202
203## Time-dependent prediction
205For Cox PH:
207\[
208S(t \mid x) = S_0(t)^{\exp(x^\top\beta)}.
209\]
211Evaluate returned step functions on a shared, train-supported grid:
213```python
214import numpy as np
216functions = model.predict_survival_function(X_test)
217survival_probability = np.vstack([fn(times) for fn in functions])
218```
220The result is `(n_test, n_times)` and is suitable for Brier metrics when `times`
221also satisfies the metric's test/training support constraints. Extrapolation
222beyond learned event-time support is not justified.

Time-dependent prediction

For Cox PH:

\[ S(t \mid x) = S_0(t)^{\exp(x^\top\beta)}. \]

Evaluate returned step functions on a shared, train-supported grid:

import numpy as np

functions = model.predict_survival_function(X_test)
survival_probability = np.vstack([fn(times) for fn in functions])

The result is (n_test, n_times) and is suitable for Brier metrics when times also satisfies the metric's test/training support constraints. Extrapolation beyond learned event-time support is not justified.

Raw lines 203 to 223
224## Calibration and claims
226Risk ranking can remain similar after a monotone transformation while probability
227calibration changes. Therefore:
229- report C-index or dynamic AUC as discrimination;
230- report Brier score as probability prediction error;
231- inspect horizon-specific calibration separately;
232- validate on data independent of fitting and tuning;
233- do not infer treatment effects from predictive Cox coefficients;
234- do not call a model clinically useful without decision-focused evaluation.

Calibration and claims

Risk ranking can remain similar after a monotone transformation while probability calibration changes. Therefore:

  • report C-index or dynamic AUC as discrimination;
  • report Brier score as probability prediction error;
  • inspect horizon-specific calibration separately;
  • validate on data independent of fitting and tuning;
  • do not infer treatment effects from predictive Cox coefficients;
  • do not call a model clinically useful without decision-focused evaluation.
Raw lines 224 to 235
236## Metadata routing
238Current estimators expose `get_metadata_routing()`. Coxnet also exposes
239`set_predict_request(alpha=...)` for passing its optional `alpha` prediction
240argument through a meta-estimator. This only matters when:
242```python
243from sklearn import set_config
245set_config(enable_metadata_routing=True)
246```
248and an enclosing meta-estimator is expected to route that metadata. Ordinary
249`pipeline.fit(X, y)` and direct `pipeline.predict(X)` do not require enabling it.

Metadata routing

Current estimators expose get_metadata_routing(). Coxnet also exposes set_predict_request(alpha=...) for passing its optional alpha prediction argument through a meta-estimator. This only matters when:

from sklearn import set_config

set_config(enable_metadata_routing=True)

and an enclosing meta-estimator is expected to route that metadata. Ordinary pipeline.fit(X, y) and direct pipeline.predict(X) do not require enabling it.

Raw lines 236 to 250
251## Sources
253Official sources checked 2026-10-01:
255- [CoxPHSurvivalAnalysis API](https://scikit-survival.readthedocs.io/en/stable/api/generated/sksurv.linear_model.CoxPHSurvivalAnalysis.html)
256- [CoxnetSurvivalAnalysis API](https://scikit-survival.readthedocs.io/en/stable/api/generated/sksurv.linear_model.CoxnetSurvivalAnalysis.html)
257- [IPCRidge API](https://scikit-survival.readthedocs.io/en/stable/api/generated/sksurv.linear_model.IPCRidge.html)
258- [Penalized Cox user guide](https://scikit-survival.readthedocs.io/en/stable/user_guide/coxnet.html)
259- [Understanding predictions](https://scikit-survival.readthedocs.io/en/stable/user_guide/understanding_predictions.html)