CauseSpecificCox

Cause-specific Cox proportional-hazards model for competing risks.

Usage

Source

CauseSpecificCox(
    cause,
    *,
    ties="efron",
    conf_level=0.95,
)

In a competing-risks setting, a cause-specific hazard model answers “among subjects still event-free, what drives the rate of this particular event?” It does so by treating competing events as censoring: subjects who experience a different cause are removed from the risk set at the time of their competing event, and a standard Cox model is fitted on the re-coded data.

This wrapper takes a multi-state Surv response and a target cause, automatically re-codes the competing events as censored, and delegates to CoxPH. All CoxPH features (stratification, frailty, robust standard errors, diagnostics, prediction) are available on the fitted object.

Parameters

cause: Any

The cause of interest. Can be a state label (string) matching one of the states in the Surv response, or an integer cause code (1-indexed).

ties: str = "efron"

Tie-handling method passed to CoxPH: "efron" (default) or "breslow".

conf_level: float = 0.95
Confidence level for coefficient intervals (default 0.95).

Examples

Fit a cause-specific Cox model for PCM progression in the mgus2 dataset:

import greenwood as gw

mg = gw.load_dataset("mgus2", backend="polars")
cr = gw.Outcome.first_event(
    endpoints={"pcm": ("ptime", "pstat"), "death": ("futime", "death")}
)

csc = gw.CauseSpecificCox(cause="pcm").fit(cr, covariates=["age", "sex"], data=mg)
csc
CauseSpecificCox (cause-specific Cox model, cause='pcm', ties='efron')

          coef  exp(coef)  se(coef)       z       p
age    0.01304      1.013  0.008259   1.579  0.1144
sexM  -0.02514     0.9752    0.1885  -0.133  0.8939

n = 1384, events = 115
Likelihood ratio test = 2.682 on 2 df, p = 0.2616

Compare cause-specific hazard ratios for both causes side by side:

csc_death = gw.CauseSpecificCox(cause="death").fit(cr, covariates=["age", "sex"], data=mg)
gw.tidy(csc, exponentiate=True, format="polars")
shape: (2, 7)
termestimatestd_errorstatisticp_valueconf_lowconf_high
strf64f64f64f64f64f64
"age"1.0131240.0082591.5787710.1143890.9968571.029657
"sexM"0.9751760.188456-0.1333880.8938860.6740141.410902

Methods

Name Description
fit() Fit the cause-specific Cox model.

fit()

Fit the cause-specific Cox model.

Usage

Source

fit(
    surv,
    covariates=None,
    *,
    data=None,
    strata=None,
    robust=False,
    cluster=None,
    frailty=None,
    frailty_cluster=None,
    frailty_theta=0.5,
    frailty_max_iter=30,
    max_iter=30,
    tol=1e-09,
    weights=None,
)

Re-codes the multi-state response so that the target cause is treated as the event and all competing causes are treated as censored, then fits a standard CoxPH model on the re-coded data.

Parameters

surv: Surv | Outcome | str

A multi-state response: a Surv with a categorical event, an Outcome (such as gw.Outcome.first_event(...)), or a formula string such as "Surv(etime, factor(cause, c(0, 1, 2), c('censor', 'pcm', 'death'))) ~ age + sex". An Outcome or formula reads its columns from data. The formula’s right-hand side sets covariates.

covariates: Any = None

A dataframe or 2-D array of covariates. A list of column names in data also works.

data: Any = None

A data frame (pandas, Polars, PyArrow, DuckDB, a lazy frame, …) holding the columns named by the response, strata, cluster, frailty_cluster, and covariates. When surv is an Outcome or a formula, rows with a missing value in any column used are dropped before fitting. Covariate formula strings and lists of column names are also resolved here.

strata: Any = None

Passed through to CoxPH.fit().

robust: Any = None

Passed through to CoxPH.fit().

cluster: Any = None

Passed through to CoxPH.fit().

frailty: Any = None

Passed through to CoxPH.fit().

frailty_cluster: Any = None

Passed through to CoxPH.fit().

frailty_theta: Any = None

Passed through to CoxPH.fit().

frailty_max_iter: Any = None

Passed through to CoxPH.fit().

max_iter: int = 30

Newton-Raphson iteration control passed through to CoxPH.fit().

tol: int = 30

Newton-Raphson iteration control passed through to CoxPH.fit().

weights: Any = None
Case weights, one per row (a column name in data, or an array). Must be finite and strictly positive. Default is None (all weights 1).

Returns

CauseSpecificCox
The fitted estimator.

Examples

Fit a cause-specific Cox model for plasma-cell malignancy (pcm) in the mgus2 dataset. Competing events (death) are treated as censored:

import greenwood as gw

mg = gw.load_dataset("mgus2", backend="pandas")
cr = gw.Outcome.first_event(
    endpoints={"pcm": ("ptime", "pstat"), "death": ("futime", "death")}
)

csc = gw.CauseSpecificCox(cause="pcm").fit(
    cr, covariates=["age", "sex"], data=mg
)
csc
CauseSpecificCox (cause-specific Cox model, cause='pcm', ties='efron')

          coef  exp(coef)  se(coef)       z       p
age    0.01304      1.013  0.008259   1.579  0.1144
sexM  -0.02514     0.9752    0.1885  -0.133  0.8939

n = 1384, events = 115
Likelihood ratio test = 2.682 on 2 df, p = 0.2616

Exponentiated coefficients give cause-specific hazard ratios:

gw.tidy(csc, exponentiate=True, format="polars")
shape: (2, 7)
termestimatestd_errorstatisticp_valueconf_lowconf_high
strf64f64f64f64f64f64
"age"1.0131240.0082591.5787710.1143890.9968571.029657
"sexM"0.9751760.188456-0.1333880.8938860.6740141.410902