Skip to content

API reference

Auto-generated from the in-source NumPy-style docstrings. For task-oriented usage see the Quickstart and the runnable notebooks in examples/.

Forest

CompetingRiskForest

CompetingRiskForest(n_estimators: int = 100, max_depth: int = 15, min_samples_split: int = 6, min_samples_leaf: int = 3, max_features: str | int | float | None = 'sqrt', samptype: Literal['swor', 'swr'] = 'swor', sampsize: int | float | Callable[[int], int] | None = None, random_state: int | None = None, mode: str = 'default', n_bins: int = 256, time_grid: int = 200, n_jobs: int = -1, splitrule: str = 'logrankCR', cause: int = 1, cause_weights: ndarray | None = None, nsplit: int | None = None, split_ntime: int | None = DEFAULT_SPLIT_NTIME, rng_mode: str = 'numpy', equivalence: str | None = None, device: Literal['auto', 'cpu', 'cuda'] = 'auto')

Bases: BaseEstimator

Competing risks random forest.

Both modes store compact per-cause event / at-risk counts at leaves and materialize CIF (Aalen-Johansen) / CHF (Nelson-Aalen) tables lazily on first predict. mode="default" uses histogram split search with uint8-binned features; mode="reference" uses pure-NumPy exact splitting with raw (unbinned) thresholds.

Parameters:

Name Type Description Default
n_estimators int

Number of trees.

100
max_depth int

Maximum tree depth.

15
min_samples_split int

Minimum samples required to attempt a split at an internal node.

6
min_samples_leaf int

Minimum samples in each child node after a split.

3
max_features ('sqrt', 'log2', None)

Number of features considered at each split. "sqrt" and "log2" are rounded up; a float is interpreted as a fraction of n_features; None uses all features.

"sqrt"
samptype ('swor', 'swr')

Per-tree sampling scheme. "swor" draws sampsize rows without replacement (subsampling); "swr" draws sampsize rows with replacement (classic bootstrap). The default matches randomForestSRC's default (samptype="swor").

"swor"
sampsize int, float in (0, 1], callable, or None

Per-tree sample size. None resolves to the type default: round(0.632 * n) for "swor" (the randomForestSRC default sampsize = x * .632) and n for "swr" (classic full bootstrap). A float in (0, 1] is a fraction of n; an int is an absolute count; a callable(n) -> int mirrors rfsrc's functional form. For "swor" the size is capped at n; a full-n "swor" draw uses every row once in original order (deterministic, no RNG consumed) and so produces empty OOB sets — the configuration for bit-identical trees vs rfSRC (samp=matrix(1L, ...)).

Out-of-bag rows are those not drawn for a tree; OOB-dependent methods (:meth:predict_oob_risk, :meth:oob_score, OOB :meth:compute_importance) require an OOB set, i.e. "swr" or "swor" with sampsize < n.

None
random_state int or None

Seed for per-tree sampling and mtry draws. If None, results are nondeterministic.

None
mode ('default', 'reference')

"default" uses the numba-jitted histogram split kernel with uint8-binned features (recommended for production fits). "reference" uses a pure-NumPy exact split search over raw feature values (slower; mainly used for equivalence checks).

"default"
n_bins int

Number of histogram bins per feature; ignored in reference mode. Must be in [2, 256].

256
time_grid int

Max points on the shared event-time grid for compact leaf storage; ignored in reference mode.

200
n_jobs int or None

Number of threads for parallel tree building. -1 uses all available CPUs (as reported by joblib), 1 runs serially. None is accepted and treated as 1 (joblib convention). Output is bit-identical across n_jobs values for a fixed random_state.

Speedup applies only to mode="default", whose split kernel is numba-jitted and releases the GIL. mode="reference" is pure-Python recursion and GIL-bound; n_jobs > 1 will not parallelize it.

Joblib dispatch has sub-millisecond overhead per tree. For n_estimators under ~20 on small datasets, n_jobs=1 may be faster than n_jobs=-1.

-1
splitrule ('logrankCR', 'logrank')

Split criterion. "logrankCR" is the composite competing-risks log-rank (pooled across causes with Lau-inclusive at-risk); matches rfSRC 3.6.1 SURV_CR_LAU. "logrank" is the cause-specific log-rank (standard at-risk — competing-cause events remove the subject from the target-cause risk set); matches rfSRC 3.6.1 SURV_LR.

"logrankCR"
cause int

1-based cause index for splitrule="logrank". Ignored when splitrule="logrankCR" or when cause_weights is given.

1
cause_weights array-like of float or None

Per-cause weight vector of length n_causes for splitrule="logrank". When supplied, uses the weighted combination (Σ_k w_k num_k)² / (Σ_k w_k² var_k) and cause is ignored. Supported in mode="reference" only; passing this in mode="default" raises NotImplementedError (see CHANGELOG note on weighted histogram kernel deferral).

None
nsplit int or None

Number of random split-point draws per feature at each node. nsplit=0 evaluates every candidate threshold exhaustively; nsplit>0 samples that many distinct candidate split points without replacement from the observed unique values at the current node (excluding the maximum, which would leave an empty right child) — matches rfSRC's split-candidate sampling semantics. None resolves to 10 in mode="default" (rfSRC default) and 0 in mode="reference" (preserves exactness for CI ground truth).

None
split_ntime int or None

Coarse time bins for split-search log-rank evaluation in mode="default". Leaves keep full time_grid_ stats for CIF/CHF output. None disables coarsening (full time grid for splits). Ignored in mode="reference". For very small cohorts (n≲500) prefer 50 or None: undersampling dominates coarsening loss when there are few unique event times.

10
equivalence (None, 'rfsrc')

Preset for cross-library predictive alignment. "rfsrc" sets rng_mode="rfsrc_aligned", disables split-scoring time-grid coarsening (split_ntime=None), and removes the time_grid cap so all unique event times are used — matching rfSRC's default behaviour. Costs ~2-3x fit time vs the default numpy RNG path. Requires an explicit random_state. Conflicts with explicit rng_mode / split_ntime raise at fit time.

To achieve bit-identical trees vs rfSRC under a full-data fit, use these parameter mappings::

rfSRC parameter       comprisk parameter
-----------------     --------------------------------
nodesize=K         -> min_samples_split=2*K, min_samples_leaf=1
samp=matrix(1L,...) -> samptype="swor", sampsize=1.0
ntime=0            -> handled internally (all event times used)
(no max-depth)     -> max_depth=None

rfSRC's nodesize is a parent-min-size constraint: both children must reach K observations. comprisk matches this with min_samples_split=2*K (guarantees each child can reach K) and min_samples_leaf=1 (removes comprisk's own child-size floor).

Known limitation: under resampling ("swr" or subsampled "swor") a residual ~0.003 p95 ΔCIF persists because rfSRC consumes an additional RNG stream during bootstrap book-keeping (SUN-44 tracks the Phase 1d fix). For bit-identity, fit with samptype="swor", sampsize=1.0.

None
device ('auto', 'cpu', 'cuda')

Compute backend for the flat-tree path. In v0.1, "auto" resolves to "cpu" — the cuda backend is shipped as a preview and is faster only at low feature count (p ≲ 20); at typical clinical workloads (p ≈ 58) it is ~1.15x slower than cpu on the same machine because host orchestration dominates the single-tree wall. Pass device="cuda" explicitly to opt into the GPU path; the full GPU rewrite is scheduled for v1.1. "cuda" requires the optional comprisk[gpu] install and is incompatible with equivalence="rfsrc" / rng_mode="rfsrc_aligned".

"auto"

feature_importances_ property

feature_importances_

Cached result of the last compute_importance call.

Raises:

Type Description
AttributeError

If compute_importance has not been called.

predict_cif

predict_cif(X, times=None) -> ndarray

Predict cause-specific cumulative incidence (Aalen-Johansen), averaged across trees.

Returns:

Name Type Description
cif (ndarray, shape(n_samples, n_causes, n_times), float64)

Ensemble mean of per-tree leaf Aalen-Johansen CIFs. When times is provided, values are step-forward interpolated onto the requested grid (right-continuous step function; 0 before the first observed event time).

predict_chf

predict_chf(X, times=None) -> ndarray

Predict cause-specific cumulative hazard (Nelson-Aalen), averaged across trees.

Returns:

Name Type Description
chf (ndarray, shape(n_samples, n_causes, n_times), float64)

Ensemble mean of per-tree leaf Nelson-Aalen CHFs. When times is provided, values are step-forward interpolated onto the requested grid (right-continuous step function; 0 before the first observed event time). On the default flat-tree path the CHF leaf table is materialised lazily on first call from raw counts persisted at fit time, then cached on the tree.

predict_risk

predict_risk(X, cause: int = 1, kind: str = 'integrated_chf') -> ndarray

Per-subject risk scalar for cause-specific concordance scoring.

Parameters:

Name Type Description Default
X (array - like, shape(n_samples, n_features))
required
cause int

Cause of interest (1..n_causes_).

1
kind ('integrated_chf', 'cif_last')

Risk scalar derived from the per-subject CIF/CHF curve:

  • "integrated_chf" (default) — sum of cause-specific CHF over the training time grid. Mirrors rfSRC's predict$predicted[, cause] convention. Better Harrell C-index discrimination than cif_last when many subjects share similar end-of-followup CIF but different hazard accumulation paths.
  • "cif_last" — CIF_cause at the last training time. Use when you want an absolute "probability of cause-k event by end of follow-up" instead of a ranking score.

For Uno IPCW C-index neither scalar dominates.

"integrated_chf"

Returns:

Name Type Description
risk (ndarray, shape(n_samples), float64)

predict_oob_risk

predict_oob_risk(cause: int = 1) -> ndarray

Per-row OOB ensemble integrated-CHF risk on the training set.

For each training row, averages cause-specific integrated CHF over only the trees where that row was out-of-bag. Mirrors rfSRC's predict$predicted.oob[, cause] convention. Requires an OOB set, i.e. samptype="swr" or samptype="swor" with sampsize < n.

Rows in-bag for every tree (probability ~0.37**n_estimators, i.e. vanishingly small for n_estimators >= 100) get a risk of 0.

oob_score

oob_score(cause: int = 1) -> float

OOB Harrell C-index on the training set for cause.

Computed against the cached training outcomes using the OOB integrated-CHF risk from :meth:predict_oob_risk. Requires an OOB set (samptype="swr" or "swor" with sampsize < n).

score

score(X, time, event=None, cause: int = 1, kind: str = 'integrated_chf') -> float

Cause-specific Harrell C-index. kind forwards to predict_risk.

Accepts either the three-positional legacy form score(X, time, event) or the sklearn-friendly score(X, y) where y is a structured array with time and event fields (see :class:comprisk.Surv).

predict

predict(X) -> ndarray

sklearn-style alias for predict_risk(X, cause=1).

Returned shape (n_samples,). The cause-1 default lets comprisk slot into Pipeline / cross_val_predict without a wrapper; for cause-k risk or for CIF / CHF curves, call :meth:predict_risk / :meth:predict_cif / :meth:predict_chf directly.

compute_importance

compute_importance(X_eval=None, y_eval=None, *, causes: list[int] | None = None, n_repeats: int = 5, random_state: int | None = None, n_jobs: int | None = None)

Compute per-cause + composite permutation variable importance.

Two flavours, dispatched by whether an evaluation set is supplied:

  • OOB Breiman (X_eval=None and y_eval=None): scored on the cached training data using the Uno IPCW C-index over each tree's out-of-bag rows. Requires an OOB set (samptype="swr" or "swor" with sampsize < n).
  • Held-out: standard sklearn permutation importance with a per-cause Wolbers-C-index scorer.

Parameters:

Name Type Description Default
X_eval (array - like, shape(n_samples, n_features))

Held-out features. If both X_eval and y_eval are None, OOB importance is computed instead.

None
y_eval structured array with fields ``time`` and ``event``

Held-out survival outcomes.

None
causes list of int

Causes to score. Defaults to range(1, n_causes_ + 1).

None
n_repeats int

sklearn.inspection.permutation_importance n_repeats. Held-out mode only; ignored in OOB mode (single permutation per tree per feature).

5
random_state int

Seed for permutation draws (reproducibility).

None
n_jobs int

Override forest.n_jobs for the per-feature parallel layer in OOB mode.

None

Returns:

Type Description
pd.DataFrame with columns ``feature``, ``cause_{k}_vimp`` for each
fitted cause in numeric order, and ``composite_vimp``.

Raises:

Type Description
ValueError

OOB mode requires an OOB set (samptype="swr" or "swor" with sampsize < n).

TypeError

Held-out mode requires y_eval to be a structured array with time and event fields.

Notes

VIMP scales as n_features * n_repeats * n_causes calls to predict_risk in held-out mode (each call walks every tree in Python). For wide cohorts the wall time can be material; downsample X_eval or use OOB mode if cost is a concern.

minimal_depth

minimal_depth(threshold: str = 'md', *, aggregation: str = 'forest', return_extra: bool = False)

Ishwaran-style minimal-depth variable selection.

A variable's minimal depth in a tree is the depth of the highest split that uses it (root = depth 0). Variables never split on get a sentinel depth of D_T where D_T is the tree's max depth (Ishwaran et al. 2010, JASA, Eq. (2)). Smaller mean minimal depth across the forest indicates a more important variable.

The selection threshold is E[Dv] computed once from the forest-averaged node-count vector l_bar_d and average tree depth D_bar, per Ishwaran et al. (2010, JASA, Theorem 1 + Section 3).

Parameters:

Name Type Description Default
threshold 'md'

Selection threshold rule. Only mean-minimal-depth "md" is supported.

"md"
aggregation ('forest', 'tree')

How the null threshold is aggregated across trees. "forest" (default) averages the node-count geometry first, then computes E[md] once (Ishwaran et al. 2010 Section 3; = rfSRC conservative=TRUE). "tree" computes E[md] per tree then averages (= rfSRC's default conservative=FALSE, minus its ad-hoc -0.5 guard). Rankings are unaffected; only the scalar threshold shifts. See :func:compute_minimal_depth for details.

"forest"
return_extra bool

If True, additionally include min_depth_q25, min_depth_q75, frac_trees_used columns.

False

Returns:

Type Description
DataFrame

Sorted ascending by mean_min_depth. Columns: feature, mean_min_depth, threshold, selected.

Raises:

Type Description
NotFittedError

If the forest has not been fitted.

ValueError

If threshold is not "md" or aggregation is invalid.

Notes

The default aggregation="forest" follows Ishwaran et al. 2010 JASA Section 3 (also 2011 SADM Definition 2): average the geometry across trees, then compute E[md] once. The 2010 paper uses only this; the tree-averaged variant is a later randomForestSRC software addition and its default (conservative=FALSE). Pass aggregation="tree" to switch. Numeric threshold values may differ from a default rfSRC run even with equivalence='rfsrc'.

shap_values

shap_values(X, *, times=None, time_aggregate=None, n_jobs=-1)

TreeSHAP values for cause-specific CIF.

Uses exact polynomial-time TreeSHAP (Lundberg et al. 2018), adapted to competing-risk forests where each leaf value is a (n_causes, n_times) CIF tensor.

Parameters:

Name Type Description Default
X (array - like, shape(n_samples, n_features))

Samples to explain.

required
times array-like of float or None

Time points at which to evaluate SHAP. If None, the model's unique_times_ grid is used.

None
time_aggregate (None, 'sum', 'trapezoid')

Collapse the time axis to a single scalar per cause before the attribution, returning a "risk-score" SHAP whose values already sum (over features) to the aggregated CIF. Because SHAP is linear in the leaf value, shap_values(time_aggregate="sum") equals shap_values().sum(axis=2) but is computed without ever materialising the per-time-point tensor (much faster and lighter).

  • "sum" — sum of CIF over the time grid (times if given, else unique_times_). Grid-dependent, like :meth:predict_risk 's "integrated_chf" sum convention but on the CIF.
  • "trapezoid" — trapezoidal integral of CIF over the same grid (grid-spacing aware).
None
n_jobs int

Threads for the parallel-over-samples TreeSHAP kernel (-1 = all cores, None/1 = serial). Independent of the fit-time n_jobs; the result is bit-identical across thread counts.

-1

Returns:

Name Type Description
shap_values ndarray

Cause-specific CIF SHAP attributions. Shape (n_samples, n_features, n_times_out, n_causes) when time_aggregate is None — for a fixed (time, cause) slice this is compatible with shap.summary_plot — or (n_samples, n_features, n_causes) when a time_aggregate is requested.

base_value ndarray

Expected (aggregated) CIF for the empty conditioning set (training-distribution baseline), averaged across trees. Shape (n_times_out, n_causes) or (n_causes,) to match shap_values.

Additivity holds point-wise:

.. math::

sum_d shap_{s,d,t,c} + base_{t,c}
approx predict_cif(X_s)_{c,t}

Subdistribution-hazard regression

FineGrayRegression

FineGrayRegression(*, cause: int = 1, cencode: int = 0, max_iter: int = 10, gtol: float = 1e-06, robust_se: bool = False)

Fine-Gray subdistribution-hazard regression for competing risks.

Fits the proportional subdistribution-hazards model of Fine & Gray (1999) via Newton-Raphson on the IPCW-weighted Breslow partial likelihood. Targets parity with R cmprsk::crr() defaults.

Parameters:

Name Type Description Default
cause int

Cause-of-interest event code (cmprsk's failcode).

1
cencode int

Censoring event code (cmprsk's cencode).

0
max_iter int

Maximum Newton iterations (cmprsk default).

10
gtol float

Convergence tolerance on max(|score| * max(|beta|, 1)).

1e-6
robust_se bool

If True, compute cluster-robust sandwich SE via per-subject score residuals; agrees with cmprsk's IPCW-corrected sandwich SE to ~1e-3 (Geskus 2011 equivalence). When False, se_ is the naive plug-in sqrt(diag(inv(info))).

False

Attributes:

Name Type Description
coef_ (ndarray, shape(n_features))
se_ (ndarray, shape(n_features))
var_ (ndarray, shape(n_features, n_features))
n_iter_ int
converged_ bool
log_likelihood_ float

Maximised partial log-likelihood at coef_.

log_likelihood_null_ float

Partial log-likelihood at beta = 0.

Examples:

>>> import numpy as np
>>> from comprisk import FineGrayRegression, Surv
>>> rng = np.random.default_rng(0)
>>> n = 200
>>> X = rng.normal(size=(n, 3))
>>> time = rng.exponential(1.0, size=n) + 0.1
>>> event = rng.choice([0, 1, 2], size=n, p=[0.3, 0.5, 0.2])
>>> y = Surv.from_arrays(event=event, time=time)
>>> fg = FineGrayRegression().fit(X, y)
>>> fg.coef_.shape
(3,)

fit

fit(X, y=None, time=None, event=None, *, cengroup=None) -> FineGrayRegression

Fit the Fine-Gray model.

Parameters:

Name Type Description Default
X (array - like, shape(n_samples, n_features))
required
y structured array

Surv-style structured array with event and time fields. Mutually exclusive with the legacy time/event kwargs.

None
time (array - like, shape(n_samples))

Legacy three-argument form fit(X, time=, event=) mirroring CompetingRiskForest.

None
event (array - like, shape(n_samples))

Legacy three-argument form fit(X, time=, event=) mirroring CompetingRiskForest.

None
cengroup array-like of int, shape (n_samples,)

Censoring stratum for each subject (cmprsk cengroup). None (default) places all subjects in a single stratum.

None

Returns:

Type Description
self

predict

predict(X) -> ndarray

Linear predictor X @ coef_.

predict_cumulative_incidence

predict_cumulative_incidence(X, times=None) -> ndarray

Predicted cumulative incidence F(t | x).

Uses the cmprsk formula F(t|x) = 1 - exp(-Λ̂_0(t) * exp(x' β)) where Λ̂_0 is the cumulative baseline subdistribution hazard.

Parameters:

Name Type Description Default
X (array - like, shape(n_samples, n_features))
required
times array - like

Times at which to evaluate F. Defaults to the cause-1 event-time grid from training.

None

Returns:

Name Type Description
F (ndarray, shape(n_samples, n_times))

PenalizedFineGrayRegression

PenalizedFineGrayRegression(*, penalty: str = 'lasso', l1_ratio: float = 1.0, gamma: float | None = None, n_lambda: int = 100, lambda_min_ratio: float = 0.001, lambdas=None, standardize: bool = True, cause: int = 1, cencode: int = 0, cv: int | None = None, cv_random_state: int | None = None, n_jobs: int | None = None, max_iter: int = 1000, tol: float = 0.0001)

Bases: BaseEstimator

Penalized Fine-Gray subdistribution-hazard regression.

Fits the proportional subdistribution-hazards model with a sparsity- or shrinkage-inducing penalty by cyclic coordinate descent on the IPCW-weighted partial likelihood, warm-started along a lambda path. Mirrors the algorithm of Fu et al. (2017) / Kawaguchi et al. (2021).

Parameters:

Name Type Description Default
penalty ('lasso', 'ridge', 'elasticnet', 'mcp', 'scad')

Penalty family. "ridge" forces l1_ratio = 0; "elasticnet" blends L1 and L2 by l1_ratio; "mcp" / "scad" are the nonconvex penalties of Zhang (2010) / Fan & Li (2001), optionally admixed with a ridge term by l1_ratio.

"lasso"
l1_ratio float

Elastic-net mixing alpha: penalty is lambda (alpha * <penalty> + (1 - alpha) * t^2 / 2). Ignored when penalty == "ridge" (treated as 0). Must be in (0, 1] otherwise.

1.0
gamma float or None

Concavity parameter for MCP (> 1; default 2.7) and SCAD (> 2; default 3.7). Ignored for the convex penalties.

None
n_lambda int

Number of lambda values on the auto-generated path.

100
lambda_min_ratio float

Smallest lambda as a fraction of lambda_max.

0.001
lambdas array - like or None

Explicit lambda grid; overrides n_lambda / lambda_min_ratio. Sorted to descending order internally.

None
standardize bool

Center and scale covariates to unit variance before fitting; the penalty then acts on the standardized coefficients (coefficients are reported on the original scale).

True
cause int

Cause-of-interest event code.

1
cencode int

Censoring event code.

0
cv int or None

If a positive integer K, run K-fold cross-validation on the weighted cross-validated partial-likelihood deviance and select lambda; otherwise lambda is chosen by BIC over the path.

None
cv_random_state int or None

Seed for the cross-validation fold split.

None
n_jobs int or None

Parallelism for the cross-validation folds (joblib); None -> serial, -1 -> all cores. The selected lambda is unaffected by scheduling. Unused when cv is None.

None
max_iter int

Maximum coordinate-descent sweeps per lambda.

1000
tol float

Relative-change convergence tolerance.

1e-4

Attributes:

Name Type Description
coef_ (ndarray, shape(n_features))

Coefficients at the selected lambda.

se_ (ndarray, shape(n_features))

Sandwich standard errors at the selected lambda.

lambda_ float

Selected penalty value.

lambda_index_ int

Index of the selected lambda in lambdas_.

coef_path_ (ndarray, shape(n_features, n_lambda))

Coefficients along the full path (original scale).

se_path_ (ndarray, shape(n_features, n_lambda))

Sandwich SEs along the path.

lambdas_ (ndarray, shape(n_lambda))

The lambda grid (descending).

deviance_path_ (ndarray, shape(n_lambda))

-2 * partial-loglik along the path.

null_deviance_ float

Deviance at beta = 0.

bic_path_ (ndarray, shape(n_lambda))

deviance + df * log(n) along the path (df = #nonzero).

n_iter_path_ ndarray of int, shape (n_lambda,)
converged_path_ ndarray of bool, shape (n_lambda,)
lambda_min_ float or None

CV-deviance-minimizing lambda (only when cv is set).

lambda_1se_ float or None

Largest lambda within one SE of the CV minimum.

cv_deviance_ ndarray or None

Mean CV deviance per lambda.

cv_deviance_se_ ndarray or None

Standard error of the CV deviance per lambda.

Examples:

>>> import numpy as np
>>> from comprisk import PenalizedFineGrayRegression, Surv
>>> rng = np.random.default_rng(0)
>>> n = 300
>>> X = rng.normal(size=(n, 8))
>>> eta = 0.7 * X[:, 0] - 0.5 * X[:, 1]
>>> time = rng.exponential(np.exp(-eta)) + 0.05
>>> event = rng.choice([0, 1, 2], size=n, p=[0.3, 0.5, 0.2])
>>> y = Surv.from_arrays(event=event, time=time)
>>> fit = PenalizedFineGrayRegression(penalty="lasso", cv=5,
...                                   cv_random_state=0).fit(X, y)
>>> fit.coef_.shape
(8,)

fit

fit(X, y=None, *, time=None, event=None, cengroup=None) -> PenalizedFineGrayRegression

Fit the penalized Fine-Gray model.

Parameters:

Name Type Description Default
X (array - like, shape(n_samples, n_features))
required
y structured array

Surv-style structured array with event and time fields. Mutually exclusive with time / event.

None
time (array - like, shape(n_samples))

Legacy three-argument form fit(X, time=, event=).

None
event (array - like, shape(n_samples))

Legacy three-argument form fit(X, time=, event=).

None
cengroup array-like of int, shape (n_samples,)

Censoring stratum for each subject. None -> single stratum.

None

Returns:

Type Description
self

predict

predict(X) -> ndarray

Linear predictor X @ coef_ at the selected lambda.

predict_cumulative_incidence

predict_cumulative_incidence(X, times=None) -> ndarray

Predicted cumulative incidence F(t | x) at the selected lambda.

F(t|x) = 1 - exp(-Lambda_0(t) exp(x' beta)) with Lambda_0 the Breslow cumulative baseline subdistribution hazard.

Parameters:

Name Type Description Default
X (array - like, shape(n_samples, n_features))
required
times array - like

Times at which to evaluate; defaults to the training cause-of-interest event-time grid.

None

Returns:

Name Type Description
F (ndarray, shape(n_samples, n_times))

coef_at

coef_at(lambda_index: int | None = None, *, lambda_value: float | None = None) -> ndarray

Coefficients at a specific path index or (nearest) lambda value.

Cause-specific hazard regression

CauseSpecificCox

CauseSpecificCox(*, cause: int = 1, max_iter: int = 25, gtol: float = 1e-09)

Cause-specific Cox proportional-hazards regression.

Fits a Cox PH model with the cause-specific censoring rule: subjects experiencing a competing event are censored at that event time. Parity target: survival::coxph(Surv(time, event == cause) ~ X, method="breslow").

Parameters:

Name Type Description Default
cause int

Cause-of-interest event code. All other positive event codes are competing events; 0 denotes administrative censoring. Both non-cause categories receive identical (censored) treatment.

1
max_iter int
25
gtol float
1e-9

Attributes:

Name Type Description
coef_ (ndarray, shape(n_features))
se_ (ndarray, shape(n_features))
var_ (ndarray, shape(n_features, n_features))
n_iter_ int
converged_ bool
log_likelihood_ float
log_likelihood_null_ float

Examples:

>>> import numpy as np
>>> from comprisk import CauseSpecificCox, Surv
>>> rng = np.random.default_rng(0)
>>> n = 300
>>> X = rng.normal(size=(n, 3))
>>> time = rng.exponential(1.0, size=n)
>>> event = rng.choice([0, 1, 2], size=n, p=[0.3, 0.5, 0.2])
>>> y = Surv.from_arrays(event=event, time=time)
>>> cs = CauseSpecificCox(cause=1).fit(X, y)
>>> cs.coef_.shape
(3,)

fit

fit(X, y=None, time=None, event=None) -> CauseSpecificCox

Fit cause-specific Cox PH.

Parameters:

Name Type Description Default
X (array - like, shape(n_samples, n_features))
required
y structured array

Surv array carrying event and time fields.

None
time array - like

Legacy three-argument form.

None
event array - like

Legacy three-argument form.

None

predict

predict(X) -> ndarray

Linear predictor X @ coef_.

Non-parametric estimation & testing

CumulativeIncidence

CumulativeIncidence(cause_codes: list[int] | None = None)

Aalen-Johansen cumulative incidence estimator with Pepe variance.

Estimates the cause-specific cumulative incidence function and pointwise variance for one or more competing event types, optionally stratified by a grouping variable.

Parameters:

Name Type Description Default
cause_codes list of int

Positive integer event codes to fit. If None (default) the codes are inferred from the data as the sorted unique positive values of event.

None

Attributes:

Name Type Description
curves_ dict

Maps (group, cause) to a :class:CIFCurve. When group is not supplied, keys take the form (None, cause).

Notes

Event coding: 0 denotes censoring; positive integers index the competing causes. The estimator is consistent under independent right-censoring with discrete or continuous event-time distributions (Aalen & Johansen, 1978).

fit

fit(time: ndarray | None = None, event: ndarray | None = None, *, group: ndarray | None = None) -> CumulativeIncidence

Fit the estimator.

Parameters:

Name Type Description Default
time array-like of shape (n,)

Observed times.

None
event array-like of shape (n,)

Event indicators (0 = censored, positive integers = causes).

None
group array-like of shape (n,)

Grouping variable. When supplied, a separate CIF is fit per (group, cause) pair.

None

Returns:

Name Type Description
self CumulativeIncidence

Fitted estimator. self.curves_ exposes the fitted curves.

timepoints

timepoints(t) -> tuple[ndarray, ndarray]

Evaluate every fitted curve at a common set of query times.

Curves are returned in a deterministic order: sorted by (str(group), cause) so that single-group fits and grouped fits share the same key convention.

Parameters:

Name Type Description Default
t array - like

Query times.

required

Returns:

Name Type Description
est np.ndarray of shape (n_curves, n_t)

Cumulative incidence at the query times.

var np.ndarray of shape (n_curves, n_t)

Pointwise variance at the query times.

gray_test

Gray's K-sample test for equality of cumulative incidence functions.

Gray's test (Gray, 1988) is the cumulative-incidence analogue of the log-rank test for survival data. It compares the cause-specific cumulative incidence functions F_{1,g}(t) across K >= 2 groups under right-censored competing-risks observation. The null hypothesis is that the CIF for the cause of interest is the same across all groups.

The test statistic is built from a (K-1)-dimensional score vector S and a (K-1) x (K-1) covariance matrix V accumulated over the unique observed times. S measures, for each non-pivot group, the weighted divergence of the group's cause-1 hazard from the pooled subdistribution-hazard prediction. V is the covariance estimator obtained from counting-process martingale theory by tracking how each group's cumulative-influence row contributes to score variance through cause-1 events and through the censoring induced by competing events. The Wald-type quadratic form T = S^T V^{-1} S is asymptotically chi-square with K-1 degrees of freedom under the null.

This module is a clean-room implementation written directly from the mathematical statement of Gray's procedure plus standard counting-process martingale theory; no GPL-licensed third-party source code (Fortran, R, or otherwise) was consulted while writing it. Variable names follow the statistical literature.

References

Gray, R.J. (1988). "A class of K-sample tests for comparing the cumulative incidence of a competing risk." Annals of Statistics 16(3):1141-1154.

Andersen, P.K., Borgan, O., Gill, R.D., Keiding, N. (1993). Statistical Models Based on Counting Processes. Springer.

GrayTestResult dataclass

GrayTestResult(stat: float, pvalue: float, df: int, score: ndarray, var: ndarray, n_groups: int, rho: float)

Outcome of :func:gray_test.

Attributes:

Name Type Description
stat float

Wald-type chi-square statistic S^T V^{-1} S.

pvalue float

Upper-tail p-value from a chi-square distribution with df degrees of freedom.

df int

Degrees of freedom, equal to K - 1 for K groups.

score np.ndarray of shape (K-1,)

Score vector for the non-pivot groups.

var np.ndarray of shape (K-1, K-1)

Estimated covariance matrix of score.

n_groups int

Number of distinct groups K.

rho float

Weight exponent rho actually used; the per-time weight is (1 - Fbar(t-))^rho.

gray_test

gray_test(time, event, group, *, cause: int = 1, rho: float = 0.0) -> GrayTestResult

Gray's K-sample test for equality of cumulative incidence functions.

Tests the null hypothesis that the cumulative incidence function for the cause of interest is the same across all groups, against the alternative that at least one group differs. Censored observations and competing events are handled via the subdistribution-hazard formulation of Gray (1988).

Parameters:

Name Type Description Default
time array-like of shape (n,)

Observed times (event or censoring), non-negative.

required
event array-like of shape (n,)

Status code per subject. 0 denotes censoring; any positive code is an event. The code matching cause is the cause of interest; any other positive code is treated as a competing event.

required
group array-like of shape (n,)

Group labels. May be integer- or string-typed; labels are sorted and recoded to {0, ..., K-1}. Requires K >= 2.

required
cause int

Event code identifying the cause of interest.

1
rho float

Power-of-pooled-survival weight exponent. The per-time weight is W(t) = (1 - Fbar(t-))^rho. rho = 0 recovers the original Gray test.

0.0

Returns:

Type Description
GrayTestResult

Statistic, p-value, degrees of freedom, score vector, score covariance, group count, and the rho actually used.

Raises:

Type Description
ValueError

If inputs have inconsistent lengths, are empty, or fewer than 2 distinct groups are present.

References

Gray, R.J. (1988). "A class of K-sample tests for comparing the cumulative incidence of a competing risk." Annals of Statistics 16(3):1141-1154.

Metrics & evaluation

concordance_index_cr

concordance_index_cr(event, time, estimate, cause: int = 1) -> float

Cause-specific concordance index for competing risks (Wolbers, 2009).

A pair (i, j) with event[i] == cause is comparable iff time[j] > time[i] and subject j did not experience a competing event at or before time[i]. For each comparable pair the estimate of subject i is compared to the estimate of subject j: a higher estimate at i is concordant, a lower one is discordant, equal estimates count as a half-concordance (tie).

Parameters:

Name Type Description Default
event array-like of int

Event/cause code per subject. 0 denotes censoring; cause is the cause of interest; any other positive integer is a competing event.

required
time array-like of float

Observed time (event or censoring) per subject.

required
estimate array-like of float

Predicted risk score for the cause of interest. Higher values should indicate higher risk.

required
cause int

The cause of interest.

1

Returns:

Type Description
float

The cause-specific concordance index. Returns 0.5 when there are no comparable pairs or no events of cause.

Raises:

Type Description
ValueError

If cause is strictly larger than every observed event code.

concordance_index_uno_cr

concordance_index_uno_cr(event, time, estimate, *, cause: int, weights: ndarray) -> float

Cause-specific Uno IPCW concordance index for competing risks.

Combines the Wolbers (2009) cause-specific pair structure with inverse-probability-of-censoring weighting (Uno, 2011). Returns the raw ratio numerator / denominator (not 1 - num/denom).

Parameters:

Name Type Description Default
event array - like

Per-subject event code, observed time, and predicted risk score for the cause of interest.

required
time array - like

Per-subject event code, observed time, and predicted risk score for the cause of interest.

required
estimate array - like

Per-subject event code, observed time, and predicted risk score for the cause of interest.

required
cause int

Cause of interest (keyword-only).

required
weights ndarray

IPCW weights, e.g. as produced by :func:compute_uno_weights.

required

Returns:

Type Description
float

Concordance value, or NaN if there are fewer than two retained observations or the denominator is zero.

compute_uno_weights

compute_uno_weights(time, event, *, gmin: float | str = 'auto', ess_frac: float = 0.2, ess_min: int = 20, eps: float = 1e-12, eps_keep: float | None = None) -> ndarray

Per-observation IPCW weights using the KM censoring estimator.

For each subject i the weight is 1 / G(time[i]^-)^2 (Uno, 2011) where G is the KM-of-censoring under a competing-risks events-first tie convention. Subjects whose left-limit G falls below the chosen lower clip gmin keep the data row alive with a tiny eps_keep weight rather than being silently dropped.

Parameters:

Name Type Description Default
time array-like of float

Observed time per subject.

required
event array-like of int

Event/cause code per subject. 0 denotes censoring.

required
gmin float | {'auto', 'none'}

Lower clip for the censoring survivor. "none" disables gating; "auto" chooses an ESS-stable clip from the event-only G distribution (Cole & Hernán, 2008). A non-finite or negative numeric value is treated as 0.0.

"auto"
ess_frac float

Effective-sample-size fraction target for gmin="auto".

0.20
ess_min int

Effective-sample-size minimum target for gmin="auto".

20
eps float

Floor used when squaring G to avoid division by zero.

1e-12
eps_keep float

Weight assigned to gated-out rows. Defaults to np.finfo(np.float64).eps.

None

Returns:

Type Description
ndarray

float64 array of length n.

score_cr

score_cr(predictions: Mapping[str, ndarray], test_time: ndarray, test_event: ndarray, eval_times: ndarray, *, cause: int = 1, metrics: Sequence[str] = ('auc', 'brier'), n_bootstrap: int = 0, confidence_level: float = 0.95, n_jobs: int = -1, random_state: int | None = None, calibration_at: Sequence[float] | None = None, calibration_n_bins: int = 10) -> ScoreResult

Competing-risks time-dep AUC, Brier, IBS, iAUC.

One-call replacement for the AUC/Brier block of R riskRegression::Score in CR mode. Accepts an arbitrary number of candidate models as a dict of name to (n_test, n_eval_times) CIF probability matrix at the cause of interest.

Parameters:

Name Type Description Default
predictions Mapping[str, ndarray]

model_name -> CIF array of shape (n_test, n_eval_times) evaluated at the same eval_times.

required
test_time array-like of float

Observed time per test subject.

required
test_event array-like of int

Event code per test subject. 0 is censoring; cause is the cause of interest; any other positive integer is a competing event.

required
eval_times array-like of float

Times at which the metrics are evaluated. Must align with the columns of every prediction matrix.

required
cause int

Cause of interest.

1
metrics sequence of str

Subset of {"auc", "brier"}. iAUC is reported when "auc" is present; IBS when "brier" is present.

``("auc", "brier")``
n_bootstrap int

Number of bootstrap resamples for 95% CIs. 0 skips CI computation and leaves lower/upper as NaN.

0
confidence_level float
0.95
n_jobs int

Number of parallel workers for the bootstrap loop.

-1
random_state int or None

Seed for the bootstrap.

None
calibration_at sequence of float

When supplied, populates ScoreResult.calibration by delegating to :func:calibration_cr at these times. The prediction columns at each calibration_at time are taken from the matching eval_times column; every entry of calibration_at must be present in eval_times.

None
calibration_n_bins int

Number of quantile bins for the calibration block.

10

Returns:

Type Description
ScoreResult

calibration_cr

calibration_cr(predictions: Mapping[str, ndarray], test_time: ndarray, test_event: ndarray, eval_times: ndarray, *, cause: int = 1, n_bins: int = 10, confidence_level: float = 0.95) -> DataFrame

Quantile-decile calibration plot data with per-bin Wilson CI.

Tidy / long-form one-call replacement for the R riskRegression::plotCalibration(method="quantile", q=10) block. For every (model, eval_time) pair the predicted CIF values are partitioned into n_bins quantile bins; per bin the predicted midpoint is the bin mean of predicted CIF, the observed frequency is the Aalen-Johansen empirical cumulative incidence (cause of interest) fit on the bin's subjects and evaluated at the eval time, and the confidence interval is a textbook Wilson score interval.

Parameters:

Name Type Description Default
predictions Mapping[str, ndarray]

model_name -> CIF array of shape (n_test, n_eval_times) evaluated at the same eval_times.

required
test_time array - like

Held-out fold time and event code (0 = censoring; cause = cause of interest; other positive ints = competing events).

required
test_event array - like

Held-out fold time and event code (0 = censoring; cause = cause of interest; other positive ints = competing events).

required
eval_times array-like of float

Times at which calibration is evaluated. The same eval-time column index is used across all models.

required
cause int
1
n_bins int

Number of quantile bins per (model, time). Mirrors R's q parameter.

10
confidence_level float

Confidence level for the Wilson score interval.

0.95

Returns:

Type Description
DataFrame

Long-form, one row per (model, eval_time, bin). Columns: model, times, predicted_decile, observed_freq, lower_ci, upper_ci, bin_n. Rows for empty bins are dropped.

Data helpers

Surv

Build the structured survival y array sklearn workflows want.

Mirrors :class:sksurv.util.Surv. The returned array has named fields event and time and shape (n,) — sliceable by sklearn cross-validation utilities, picklable, copy-equivalent to a pair of 1-D arrays.

Examples:

>>> import numpy as np
>>> from comprisk import Surv
>>> y = Surv.from_arrays(event=[0, 1, 2, 0], time=[1.0, 2.0, 3.0, 0.5])
>>> y.dtype.names
('event', 'time')

from_arrays staticmethod

from_arrays(event, time, *, name_event: str = 'event', name_time: str = 'time') -> ndarray

Pack event and time 1-D arrays into a structured array.

Parameters:

Name Type Description Default
event (array - like, shape(n))

Event indicator. 0 for censored, 1..K for cause-of-event in competing-risks data.

required
time (array - like, shape(n))

Observed time-to-event or censoring.

required
name_event str

Field names in the structured array. Defaults match sksurv.

'event'
name_time str

Field names in the structured array. Defaults match sksurv.

'event'

Returns:

Name Type Description
y structured ndarray, shape (n,)

Fields (name_event, name_time), dtypes int64 and float64.