← Về thư mục
📄 / / profiles / r-and-d / raw_evidence / scientific-agent-skills / skills / scikit-survival / references / cox-models.md

Cox, Coxnet, and IPC ridge models

Verified for scikit-survival 0.28.0 on 2026-07-23.

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:

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.

Stability and interpretation

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.

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:

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,
)

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.

Feature selection is uncertain

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

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_log_time = model.predict(X_test_scaled)

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.

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.

Calibration and claims

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

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.

Sources

Official sources checked 2026-07-23: