Skip to content

ChimeraBoostQuantileRegressor

A whole grid of conditional quantiles from a single booster, with levels that cannot cross. See the User Guide: predictive distributions.

Bases: BaseEstimator

Gradient boosting for a whole predictive distribution at once.

One booster, one tree structure per round, and a K-vector in every leaf, one entry per level in quantiles. Against one quantile regressor per level that is roughly K times less split-search work, and the predictions cannot cross: the 30% quantile is never returned above the 70%.

Ordering is enforced per row by monotone rearrangement of the delivered scores. It is exact for every row and holds at every intermediate stage of staged_predict. Rearrangement cannot cost accuracy -- sorting a crossing quantile curve never increases pinball loss at any level (Chernozhukov, Fernandez-Val & Galichon 2010).

Deliberately not a RegressorMixin: predict returns a matrix, so the inherited score (which assumes one number per row) would be wrong. score here is negative CRPS, so higher is better, as sklearn requires.

Read more in the User Guide.

Parameters:

Name Type Description Default
quantiles array - like or None

Ascending, unique levels strictly inside (0, 1). Default 0.05, 0.10, ... 0.95. Column k of predict is level k.

None
split_projection (rotate, sum, gram)

How split gain is scored across the quantile levels. Keep the default unless you are experimenting. "rotate" alternates a location and a spread contrast, two location rounds per spread round, and measured best. "sum" adds the levels up, which makes it blind to a change in spread. "gram" picks the strongest contrast each round and measured no better than rotating.

"rotate"
exact_splits bool

Score splits exactly across every level instead of on a projection. More faithful, but the fit gets slower and more memory-hungry as the grid grows -- a reference setting, not one for routine use.

False
conformalize (auto, True, False)

Calibrate the intervals by conformalized quantile regression, as a per-level scale about the predicted median. True carves calibration_fraction of the rows off BEFORE the early-stopping split, so that fold influences neither the fit nor the stopping point, and calibrates on it; raises if the fold is too small to certify the requested levels. "auto" spends no extra rows: after the fit it calibrates on the evaluation rows early stopping already held out (the user's eval_set, or the carved fold), and keeps the raw grid when there are none or the fold cannot certify the grid. Unlike True, those rows also chose the stopping round, so "auto" carries no formal coverage guarantee. False returns the raw grid.

"auto"
calibration_fraction float

Share of training rows reserved for conformalization. Ignored unless conformalize=True.

0.2
audition bool

Choose per fit among five candidates on the evaluation rows and keep the lowest CRPS. H is the head as configured; B is the same booster with max_bins=254 (skipped when max_bins is already 254 or more); R is H's raw grid moved onto a squared-error centre (a default ChimeraBoostRegressor plus the median evaluation residual) and then calibrated; S ("fixed") is the centre model's prediction plus the empirical quantiles of its evaluation residuals -- one width for every row; N ("scaled") is the centre plus a predicted spread (a second default regressor on the centre's absolute residuals) times the quantiles of the standardized evaluation residuals. Each candidate is calibrated exactly as conformalize="auto" calibrates the head and scored unweighted; ties go H, then B, then R, then S, then N. Needs conformalize="auto" and evaluation rows (the user's eval_set or the carved fold); otherwise the single head is fitted. Costs about 2.6x a single head at the median for fits with an eval_set: S reuses R's centre fit, so the extra cost over the three-candidate audition is the spread-model fit, about 10% of the fit at the median. The full-data refit adds about 30% at the median when it acts. False fits a single head, as releases before 0.33.0 did. model_ stays the fitted head booster whatever wins (the 254-bin booster for a bins win); for S and N it delivers nothing -- predict serves the centre/spread models -- but stays inspectable.

True
refit_full bool

Retrain the winner on all rows after early stopping chose the budget, as ChimeraBoostRegressor does. Acts only on the automatic early-stopping split: never with a user eval_set, with early stopping off, with conformalize=True, or when the carve failed -- those fits are exactly what they are without it. An R, S or N winner's centre is then replaced by a default ChimeraBoostRegressor fitted on all rows without an eval_set -- offsets, spread model, residual quantiles, floor and calibration factors stay from the audition fit -- and the head booster of an H, B or R winner is retrained from scratch on all rows with early stopping off, at min(ceil(t_star / (1 - validation_fraction)), n_estimators) rounds with the early-stopped learning rate pinned, where t_star is the rounds the early-stopped head kept. audition_, conformal_scale_, best_iteration_ and validation_history_ keep the early-stopped fit's values; refit_ records what was retrained. False skips the retrain.

True

Attributes:

Name Type Description
model_ MultiQuantileBoosting

The fitted head booster (the 254-bin booster for a bins win). Stays the head even when S or N wins; then it delivers nothing but stays inspectable.

quantiles_ ndarray of shape (n_quantiles,)

The resolved grid.

conformal_scale_ ndarray of shape (n_quantiles,)

Per-level conformal scale about the predicted median; all ones when the fit left the raw grid untouched (conformalize=False, or "auto" with no certifiable evaluation rows). With an audition win, the winner's factors (R's for a recentred win).

audition_ dict or None

{"selected": "head" | "bins" | "recentred" | "fixed" | "scaled", "crps": {name: score}} for the candidates that ran, or None when no audition ran (no evaluation rows, conformalize True or False, or audition=False).

refit_ dict or None

{"centre": bool, "head": bool, "rounds": int or None} -- whether the centre and the head booster were retrained on all rows, and the head's retrained round count (None when the head was not retrained). None when nothing was retrained (refit_full=False, or a fit the refit does not cover).

Notes

Every other parameter carries its usual ChimeraBoost meaning. Two defaults are set for this head rather than inherited: depth is 6 -- deep leaves narrow the tails, which the default "auto" calibration repairs -- and min_child_weight follows a floor implied by the most extreme level on the grid.

Source code in chimeraboost/quantile_api.py
def __init__(self, quantiles=None, n_estimators=2000, learning_rate=None,
             depth=None, l2_leaf_reg=1.0, max_bins=128, subsample=1.0,
             colsample=None, cat_smoothing=1.0, cat_n_permutations=4,
             early_stopping_rounds=None, min_child_weight=None,
             thread_count=None, random_state=None, verbose=False,
             cat_features=None, cat_combinations=None,
             quantize_gradients=True, early_stopping=True,
             validation_fraction=0.2, split_projection="rotate",
             exact_splits=False, conformalize="auto",
             calibration_fraction=0.2, audition=True,
             refit_full=True):
    self.quantiles = quantiles
    self.n_estimators = n_estimators
    self.learning_rate = learning_rate
    self.depth = depth
    self.l2_leaf_reg = l2_leaf_reg
    self.max_bins = max_bins
    self.subsample = subsample
    self.colsample = colsample
    self.cat_smoothing = cat_smoothing
    self.cat_n_permutations = cat_n_permutations
    self.early_stopping_rounds = early_stopping_rounds
    self.min_child_weight = min_child_weight
    self.thread_count = thread_count
    self.random_state = random_state
    self.verbose = verbose
    self.cat_features = cat_features
    self.cat_combinations = cat_combinations
    self.quantize_gradients = quantize_gradients
    self.early_stopping = early_stopping
    self.validation_fraction = validation_fraction
    self.split_projection = split_projection
    self.exact_splits = exact_splits
    self.conformalize = conformalize
    self.calibration_fraction = calibration_fraction
    self.audition = audition
    self.refit_full = refit_full

best_iteration_ property

best_iteration_

Best round of the winner's booster (the head's for R, the centre model's for S and N).

validation_history_ property

validation_history_

Per-round validation score from fit (empty without a validation set). The winner's booster's CRPS for a head, bins or recentred win (the head's for R); the centre model's squared-error validation loss (RMSE space) for a fixed or scaled win.

feature_importances_ property

feature_importances_

Split-gain importances from the winner's booster (the head's for R, the centre model's for S and N).

fit

fit(
    X,
    y,
    cat_features=None,
    eval_set=None,
    groups=None,
    sample_weight=None,
    callbacks=None,
)

Fit the model. Arguments carry the same meaning as ChimeraBoostRegressor.fit.

Source code in chimeraboost/quantile_api.py
def fit(self, X, y, cat_features=None, eval_set=None, groups=None,
        sample_weight=None, callbacks=None):
    """Fit the model. Arguments carry the same meaning as
    `ChimeraBoostRegressor.fit`."""
    cat_features = _resolve_cat_features(self, cat_features)
    cat_features = _resolve_cat_feature_names(cat_features, X)

    _validate_hyperparams(self)
    if self.split_projection not in ("rotate", "sum", "gram"):
        raise ValueError(
            'split_projection must be "rotate", "sum" or "gram"; got '
            f"{self.split_projection!r}.")
    _check_conformalize(self.conformalize)
    _check_audition(self.audition)
    if not 0.0 < self.calibration_fraction < 1.0:
        raise ValueError("calibration_fraction must be in (0, 1); got "
                         f"{self.calibration_fraction!r}.")

    y = _validate_fit_input(self, X, y, cat_features, sample_weight,
                            classification=False)
    if eval_set is not None:
        _check_eval_set(eval_set, self.n_features_in_)
        _check_feature_names_match(self, eval_set[0])

    taus = _resolve_quantiles(self.quantiles)
    self.quantiles_ = taus
    self._median_idx_ = _median_index(taus)
    self.conformal_scale_ = np.ones(taus.shape[0])
    self.audition_ = None
    self.refit_ = None
    self._centre_model_ = None
    self._centre_off_ = None
    self._fixed_q_ = None
    self._spread_model_ = None
    self._spread_floor_ = None
    self._scaled_q_ = None

    X = as_model_array(X, bool(cat_features))
    y = np.asarray(y, dtype=np.float64)
    if sample_weight is not None:
        sample_weight = np.asarray(sample_weight, dtype=np.float64)

    cal, X, y, sample_weight, groups = self._carve_calibration_fold(
        X, y, sample_weight, groups)
    # Kept for the optional full-data refit below: the auto split
    # reassigns X/y, but the refit retrains on every row.
    X_full, y_full, sw_full, groups_full = X, y, sample_weight, groups
    had_user_eval = eval_set is not None
    X, y, sample_weight, eval_set, es_rounds = self._carve_es_split(
        X, y, sample_weight, eval_set, groups)
    # The carve is the only path that sets eval_set when the user did
    # not, so this is True exactly when the automatic split was used.
    auto_split = not had_user_eval and eval_set is not None

    self.model_ = self._make_mq_booster(taus, es_rounds, cat_features,
                                        len(y))
    self.model_.fit(X, y, cat_features=cat_features, eval_set=eval_set,
                    sample_weight=sample_weight, callbacks=callbacks)

    # 3. Conformalize: on the pristine fold for True, or -- under
    # "auto" -- on the evaluation rows early stopping already held
    # out. conformal_scale_ is still all ones here, so predict
    # returns the raw grid.
    if cal is not None:
        mi, mw = self._median_idx_
        self.conformal_scale_ = _cqr_scales(
            self.model_.predict_raw(cal[0]), cal[1], taus, mi, mw)
    elif self._wants_audition(eval_set):
        self._run_audition(X, y, sample_weight, eval_set, es_rounds,
                           cat_features, callbacks, taus)
    elif self.conformalize == "auto" and eval_set is not None:
        mi, mw = self._median_idx_
        try:
            self.conformal_scale_ = _cqr_scales(
                self.predict(eval_set[0]),
                np.asarray(eval_set[1], dtype=np.float64),
                taus, mi, mw)
        except ValueError:
            pass   # uncertifiable: keep the raw grid, never raise

    self._maybe_refit_full(auto_split, taus, es_rounds, cat_features,
                           X_full, y_full, sw_full, groups_full)

    return self

predict

predict(
    X,
    kind="quantiles",
    alpha=None,
    thresholds=None,
    n_samples=None,
    random_state=None,
)

Predict the conditional distribution.

kind="quantiles" (default) returns (n_samples, n_quantiles), column k being level quantiles_[k], non-decreasing along axis 1.

kind="interval" returns (n_samples, 2): the central 1 - alpha interval, read off the alpha/2 and 1 - alpha/2 levels, which must both be on the grid. No interpolation -- an interval the model was not fitted for is an error, not a guess.

kind="mean" returns (n_samples,), the integral of the quantile function over tau: trapezoid across the grid plus flat extension of the edge levels out to 0 and 1. The flat extension is the honest reading of a finite grid, assuming nothing about tails the model never estimated.

kind="median" returns (n_samples,), the predicted median -- the 0.5 level when the grid carries it, interpolated between its neighbours when it does not. This is the centre conformalization rescales about.

kind="cdf" returns (n_samples, n_thresholds): P(y <= t) for each t in thresholds, by inverting the grid. A 1-D thresholds is shared by every row; a 2-D (n_samples, T) array is read row against row -- the rule is dimensionality, never length. Clamped to the outermost fitted levels rather than to 0 and 1, for the same reason "mean" extends flat. Warns when the fitted grid is too coarse to carry a CDF (any inter-level gap above 0.2).

kind="sample" returns (n_samples_rows, n_samples): inverse- transform draws from the predicted distribution, for feeding a downstream simulation. random_state seeds them; draws stay inside the fitted level range.

Unlike "interval", "cdf" and "sample" interpolate between levels. That is a different question -- reading a fitted curve at a point, rather than claiming a level was fitted when it was not.

Serves the audition winner (audition_): the head, the 254-bin head, the recentred grid, the fixed-offset grid, or the scaled grid.

Source code in chimeraboost/quantile_api.py
def predict(self, X, kind="quantiles", alpha=None, thresholds=None,
            n_samples=None, random_state=None):
    """Predict the conditional distribution.

    ``kind="quantiles"`` (default) returns (n_samples, n_quantiles),
    column k being level ``quantiles_[k]``, non-decreasing along axis 1.

    ``kind="interval"`` returns (n_samples, 2): the central ``1 - alpha``
    interval, read off the ``alpha/2`` and ``1 - alpha/2`` levels, which
    must both be on the grid. No interpolation -- an interval the model was
    not fitted for is an error, not a guess.

    ``kind="mean"`` returns (n_samples,), the integral of the quantile
    function over tau: trapezoid across the grid plus flat extension of the
    edge levels out to 0 and 1. The flat extension is the honest reading of
    a finite grid, assuming nothing about tails the model never estimated.

    ``kind="median"`` returns (n_samples,), the predicted median -- the
    0.5 level when the grid carries it, interpolated between its
    neighbours when it does not. This is the centre conformalization
    rescales about.

    ``kind="cdf"`` returns (n_samples, n_thresholds): ``P(y <= t)`` for
    each ``t`` in ``thresholds``, by inverting the grid. A 1-D
    ``thresholds`` is shared by every row; a 2-D (n_samples, T) array is
    read row against row -- the rule is dimensionality, never length.
    Clamped to the outermost fitted levels rather than to 0 and 1, for
    the same reason ``"mean"`` extends flat. Warns when the fitted grid
    is too coarse to carry a CDF (any inter-level gap above 0.2).

    ``kind="sample"`` returns (n_samples_rows, n_samples): inverse-
    transform draws from the predicted distribution, for feeding a
    downstream simulation. ``random_state`` seeds them; draws stay inside
    the fitted level range.

    Unlike ``"interval"``, ``"cdf"`` and ``"sample"`` interpolate between
    levels. That is a different question -- reading a fitted curve at a
    point, rather than claiming a level was fitted when it was not.

    Serves the audition winner (``audition_``): the head, the 254-bin
    head, the recentred grid, the fixed-offset grid, or the scaled grid.
    """
    Xv = _check_predict_input(self, X)
    Q = self._delivered_grid(X if Xv is None else Xv)

    if kind == "quantiles":
        return Q

    if kind == "interval":
        i, j = self._interval_levels(alpha)
        return np.column_stack([Q[:, i], Q[:, j]])

    if kind == "mean":
        return self._mean_from_quantiles(Q)

    if kind == "median":
        mi, mw = self._median_idx_
        return _centre(Q, mi, mw)

    if kind == "cdf":
        return self._cdf_from_quantiles(Q, thresholds)

    if kind == "sample":
        return self._sample_from_quantiles(Q, n_samples, random_state)

    raise ValueError(
        'kind must be "quantiles", "interval", "mean", "median", "cdf" '
        f'or "sample"; got {kind!r}.')

predict_thresh

predict_thresh(X, thresholds, direction='greater')

Probability of the target landing beyond thresholds.

direction="greater" returns P(y > t); "less" returns P(y <= t). Both read the same fitted quantile function as predict(kind="cdf"): linear between grid levels, clamped to the outermost fitted levels outside them -- on the default grid no probability reads below 0.05 or above 0.95, because the model never estimated those tails. A coarse grid (any inter-level gap above 0.2, e.g. quantiles=[0.1, 0.5, 0.9]) warns: the probabilities would be mostly interpolation, not estimates.

thresholds may be a scalar (one probability per row, returned 1-D), a 1-D array of T values shared by every row (returns (n_samples, T)), or a 2-D (n_samples, T) array read row against row (returns (n_samples, T)). The rule is dimensionality, never length: stack several per-row threshold lists with np.column_stack.

Serves the audition winner, like predict.

Source code in chimeraboost/quantile_api.py
def predict_thresh(self, X, thresholds, direction="greater"):
    """Probability of the target landing beyond ``thresholds``.

    ``direction="greater"`` returns ``P(y > t)``; ``"less"`` returns
    ``P(y <= t)``. Both read the same fitted quantile function as
    ``predict(kind="cdf")``: linear between grid levels, clamped to the
    outermost fitted levels outside them -- on the default grid no
    probability reads below 0.05 or above 0.95, because the model never
    estimated those tails. A coarse grid (any inter-level gap above 0.2,
    e.g. ``quantiles=[0.1, 0.5, 0.9]``) warns: the probabilities would
    be mostly interpolation, not estimates.

    ``thresholds`` may be a scalar (one probability per row, returned
    1-D), a 1-D array of T values shared by every row (returns
    (n_samples, T)), or a 2-D (n_samples, T) array read row against row
    (returns (n_samples, T)). The rule is dimensionality, never length:
    stack several per-row threshold lists with ``np.column_stack``.

    Serves the audition winner, like ``predict``.
    """
    if direction not in ("greater", "less"):
        raise ValueError(
            f'direction must be "greater" or "less"; got {direction!r}.')
    if thresholds is None:
        raise ValueError(
            "predict_thresh needs `thresholds`: the values t to compare "
            "the target against.")
    Xv = _check_predict_input(self, X)
    Q = self._delivered_grid(X if Xv is None else Xv)
    cdf = self._cdf_from_quantiles(Q, thresholds)
    if np.ndim(thresholds) == 0:
        cdf = cdf[:, 0]
    return 1.0 - cdf if direction == "greater" else cdf

staged_predict

staged_predict(X)

Yield the (n, K) quantile matrix after each successive tree. The conformal rescaling is a post-fit transform, so it is applied at every stage and the last one equals predict. For a recentred win each head stage is moved onto the final centre and scaled by R's factors. For a fixed win each centre-model stage carries the final q_s; for a scaled win each centre-model stage carries the final spread and q_n.

Serves the audition winner.

Source code in chimeraboost/quantile_api.py
def staged_predict(self, X):
    """Yield the (n, K) quantile matrix after each successive tree. The
    conformal rescaling is a post-fit transform, so it is applied at every
    stage and the last one equals ``predict``. For a recentred win each
    head stage is moved onto the final centre and scaled by R's factors.
    For a fixed win each centre-model stage carries the final q_s; for a
    scaled win each centre-model stage carries the final spread and q_n.

    Serves the audition winner."""
    Xv = _check_predict_input(self, X)
    X = X if Xv is None else Xv
    if self._is_recentred():
        yield from self._staged_recentred(X)
        return
    if self._is_fixed():
        yield from self._staged_fixed(X)
        return
    if self._is_scaled():
        yield from self._staged_scaled(X)
        return
    for staged in self.model_.staged_predict_raw(X):
        yield self._conformalize(staged)

score

score(X, y, sample_weight=None)

Negative CRPS (mean pinball loss over the grid). Higher is better, per the sklearn convention. Serves the audition winner.

Source code in chimeraboost/quantile_api.py
def score(self, X, y, sample_weight=None):
    """Negative CRPS (mean pinball loss over the grid). Higher is better,
    per the sklearn convention. Serves the audition winner."""
    return -quantile_metrics.crps(y, self.predict(X), self.quantiles_,
                                  sample_weight)

report

report(X, y, sample_weight=None, baseline=None)

quantile_metrics.quantile_report on this model's predictions: CRPS and its skill score, per-level pinball, coverage plus width plus interval score for every symmetric interval, the PIT histogram, and the crossing rate.

baseline sets what the skill score is measured against -- pass the training targets to score against the marginal distribution the model actually had, rather than the hindsight marginal of y.

Serves the audition winner.

Source code in chimeraboost/quantile_api.py
def report(self, X, y, sample_weight=None, baseline=None):
    """`quantile_metrics.quantile_report` on this model's predictions:
    CRPS and its skill score, per-level pinball, coverage plus width plus
    interval score for every symmetric interval, the PIT histogram, and
    the crossing rate.

    ``baseline`` sets what the skill score is measured against -- pass the
    training targets to score against the marginal distribution the model
    actually had, rather than the hindsight marginal of ``y``.

    Serves the audition winner.
    """
    return quantile_metrics.quantile_report(y, self.predict(X),
                                            self.quantiles_, sample_weight,
                                            baseline)

shap_values

shap_values(
    X,
    X_background=None,
    kind="quantiles",
    alpha=None,
    quantile=None,
    space="delivered",
)

Exact interventional TreeSHAP for a predicted quantile grid.

Explains what predict returned. Contributions plus expected_value_ (set by this call) reconstruct it, level by level. Serves the audition winner. For a recentred win the delivered grid is linear in the head's raw channels plus the centre prediction, so the attribution is exact: the head's mapped through v -> s * (v - centre(v)), plus the centre model's own shap_values on every level, its offset folded into the expected value. For a fixed win every level is the centre prediction plus a constant, so the attribution is the centre model's own SHAP on every level (kind="width" is exactly zero). For a scaled win each level is c(x) + s(x) * k_tau, so the attribution is the centre model's SHAP plus k_tau times the spread model's SHAP -- exact wherever the spread sits above its floor, approximate on floored rows.

kind selects the explained quantity:

  • "quantiles" -- (n_samples, n_features, n_quantiles), or (n_samples, n_features) when quantile names one fitted level.
  • "mean" -- the tau-integrated point prediction.
  • "width" -- the width of the central 1 - alpha interval: which features make this row's prediction more uncertain, as opposed to higher or lower. Shapley values are linear in the value function, so the difference of two levels' attributions is exactly the attribution of their difference.

space is for one specific job and most callers can ignore it. Predictions are rearranged on delivery, which relabels a row's levels, so the default "delivered" measures each row against its own reordering of the background and expected_value_ is (n_samples, n_quantiles). Aggregating those across rows mixes rows that were reordered differently. space="raw" explains the pre-rearrangement levels instead, against one shared (n_quantiles,) baseline, which is what makes a cross-row average meaningful -- shap_importances uses it for exactly that reason. The two agree on any row whose levels were already in order. "mean" and "width" are order-dependent by construction and always read the delivered grid.

Source code in chimeraboost/quantile_api.py
def shap_values(self, X, X_background=None, kind="quantiles", alpha=None,
                quantile=None, space="delivered"):
    """Exact interventional TreeSHAP for a predicted quantile grid.

    Explains what ``predict`` returned. Contributions plus
    ``expected_value_`` (set by this call) reconstruct it, level by level.
    Serves the audition winner. For a recentred win the delivered grid is
    linear in the head's raw channels plus the centre prediction, so the
    attribution is exact: the head's mapped through ``v -> s * (v -
    centre(v))``, plus the centre model's own ``shap_values`` on every
    level, its offset folded into the expected value. For a fixed win
    every level is the centre prediction plus a constant, so the
    attribution is the centre model's own SHAP on every level
    (``kind="width"`` is exactly zero). For a scaled win each level is
    ``c(x) + s(x) * k_tau``, so the attribution is the centre model's
    SHAP plus ``k_tau`` times the spread model's SHAP -- exact wherever
    the spread sits above its floor, approximate on floored rows.

    ``kind`` selects the explained quantity:

    * ``"quantiles"`` -- ``(n_samples, n_features, n_quantiles)``, or
      ``(n_samples, n_features)`` when ``quantile`` names one fitted level.
    * ``"mean"`` -- the tau-integrated point prediction.
    * ``"width"`` -- the width of the central ``1 - alpha`` interval: which
      features make this row's prediction more uncertain, as opposed to
      higher or lower. Shapley values are linear in the value function, so
      the difference of two levels' attributions is exactly the attribution
      of their difference.

    ``space`` is for one specific job and most callers can ignore it.
    Predictions are rearranged on delivery, which relabels a row's levels,
    so the default ``"delivered"`` measures each row against its own
    reordering of the background and ``expected_value_`` is
    ``(n_samples, n_quantiles)``. Aggregating those across rows mixes rows
    that were reordered differently. ``space="raw"`` explains the
    pre-rearrangement levels instead, against one shared
    ``(n_quantiles,)`` baseline, which is what makes a cross-row average
    meaningful -- `shap_importances` uses it for exactly that reason. The
    two agree on any row whose levels were already in order.
    ``"mean"`` and ``"width"`` are order-dependent by construction and
    always read the delivered grid.
    """
    if space not in ("raw", "delivered"):
        raise ValueError(
            f'space must be "raw" or "delivered"; got {space!r}.')
    if kind not in ("quantiles", "mean", "width"):
        raise ValueError(
            f'kind must be "quantiles", "mean" or "width"; got {kind!r}.')

    Xv = _check_predict_input(self, X)
    X = X if Xv is None else Xv
    if X_background is not None:
        # Consumed positionally, so a reordered DataFrame would silently
        # skew every baseline.
        bg = _check_predict_input(self, X_background)
        X_background = X_background if bg is None else bg
    if self._is_recentred():
        return self._shap_recentred(X, X_background, kind, alpha,
                                   quantile, space)
    if self._is_fixed():
        return self._shap_fixed(X, X_background, kind, alpha,
                               quantile, space)
    if self._is_scaled():
        return self._shap_scaled(X, X_background, kind, alpha,
                                quantile, space)
    return self._shap_single(X, X_background, kind, alpha, quantile,
                             space)

shap_importances

shap_importances(
    X,
    feature_names=None,
    n_features=None,
    prettified=False,
    quantile=None,
)

Global SHAP importance: mean(abs(shap_values(X))) per feature.

Averaged over the whole grid by default, or over one level when quantile names it. Uses space="raw" so that every row is measured on the same footing -- see shap_values -- which is what a cross-row average needs.

Returns a structured (feature, importance) array sorted descending, or a {feature: importance} dict when prettified=True.

Source code in chimeraboost/quantile_api.py
def shap_importances(self, X, feature_names=None, n_features=None,
                     prettified=False, quantile=None):
    """Global SHAP importance: ``mean(abs(shap_values(X)))`` per feature.

    Averaged over the whole grid by default, or over one level when
    ``quantile`` names it. Uses ``space="raw"`` so that every row is
    measured on the same footing -- see `shap_values` -- which is what a
    cross-row average needs.

    Returns a structured ``(feature, importance)`` array sorted descending,
    or a ``{feature: importance}`` dict when ``prettified=True``.
    """
    phi = self.shap_values(X, quantile=quantile, space="raw")
    imp = np.abs(phi).mean(axis=0)
    if imp.ndim == 2:                      # (n_features, n_quantiles)
        imp = imp.mean(axis=1)
    return _format_shap_importances(
        self, imp, feature_names=feature_names, n_features=n_features,
        prettified=prettified)