Skip to content

Summary

All global effect methods have a similar interface and workflow:

  1. create an instance of the global effect method you want to use
  2. (optional) .fit() to customize the method
  3. .plot() to plot the global effect of a feature
  4. .eval() to evaluate the global effect of a feature at a specific grid of points

Usage

# set up the input
X = ... # input data
predict = ... # model to be explained
jacobian = ... # jacobian of the model
  1. Create an instance of the global effect method you want to use:

    g_method = effector.PDP(data=X, model=predict)
    
    g_method = effector.RHALE(data=X, model=predict, model_jac=jacobian)
    
    g_method = effector.ShapDP(data=X, model=predict)
    
    g_method = effector.ALE(data=X, model=predict)
    
    g_method = effector.DerPDP(data=X, model=predict, model_jac=jacobian)
    
  2. Customize the global effect method (optional):

    .fit(features, **method_specific_args)

    This is the place for customization

    The .fit() step can be omitted if you are ok with the default settings; you can directly call the .plot(), or .eval() methods. However, if you want more control over the fitting process, you can pass additional arguments to the .fit() method. Check the Usage section below and the method-specific documentation for more information.

    Usage
    # customize the axis-partitioning (binning) method
    binning_method = effector.axis_partitioning.Agglomerative(
        init_nof_bins = 50, # start from 50 bins (default: 20)
        min_points_per_bin = 10, # minimum number of points per bin (default: 2)
    )
    g_method.fit(
        features=[0, 1], # list of features to be analyzed
        binning_method=binning_method, # custom binning method
    )
    
  3. Plot the global effect of a feature:

    .plot(feature)

    Usage
    feature = ...
    g_method.plot(feature, **plot_specific_args)
    
    Output

    Alt text

    Alt text

    Alt text

    Alt text

    Alt text

  4. Evaluate the global effect of a feature at a specific grid of points:

    .eval(feature, xs)

    Usage
    # Example input
    feature = ... # feature to be analyzed
    xs = ... # grid of points to evaluate the global effect, e.g., np.linspace(0, 1, 100)
    
    y = g_method.eval(feature, xs)
    het = g_method.eval_heter(feature, xs)
    

API

effector.global_effect_ale.ALE(data, model, *, nof_instances=10000, axis_limits=None, schema=None, random_state=21)

Bases: ALEBase

Constructor for the ALE plot.

Definition

ALE reveals the effect of \(x_s\) by accumulating, bin by bin, the average local effect of the feature. The axis of \(x_s\) is split into \(K\) fixed bins by the limits \(z_0 < z_1 < \dots < z_K\). For an instance \(x^i\) whose \(x_s^i\) falls in bin \(k\), the local effect is the secant of the model across that bin: $$ \mathtt{effect}i = \frac{f(x^i $$ where }) - f(x^i_{s=z_{k-1}})}{z_k - z_{k-1}\(x^i_{s=z}\) is \(x^i\) with its \(s\)-th coordinate set to \(z\). The bin effect is the mean local effect over the instances \(S_k\) that fall in bin \(k\), and ALE at a point \(x\) lying in bin \(k_x\) accumulates the completed bins plus the partial contribution of the current one: $$ \mu_k = \frac{1}{|S_k|} \sum_{i \in S_k} \mathtt{effect}i \qquad \hat{f}^{ALE}(x) = \sum)\, \mu_k + (x - z_{k_x - 1})\, \mu_{k_x} $$ The curve is centered afterwards (by default }^{k_x - 1} (z_k - z_{k-1zero_integral, subtracting its mean over the axis).

The heterogeneity is the variance of the local effects within the bin containing \(x\); eval_heter returns it as a step function: $$ H(x) = \sigma^2_{k_x}, \qquad \sigma^2_k = \frac{1}{|S_k|} \sum_{i \in S_k} (\mathtt{effect}_i - \mu_k)^2 $$

The std of the bin-effects is \(\sqrt{\sigma^2_k}\), drawn as the error bars on the bin plot.

Notes
  • The required parameters are data and model. The rest are optional.

Parameters:

Name Type Description Default
data ndarray

the design matrix

  • shape: (N,D)
required
model callable

the black-box model. Must be a Callable with:

  • input: ndarray of shape (N, D)
  • output: ndarray of shape (N, )
required
nof_instances Union[int, str]

the number of instances to use for the explanation

  • use an int, to specify the number of instances
  • use "all", to use all the instances
10000
axis_limits Optional[ndarray]

The limits of the feature effect plot along each axis

  • use a ndarray of shape (2, D), to specify them manually
  • use None, to be inferred from the data
None
schema Optional[Union[Schema, dict]]

input metadata (R10) — an effector.Schema or a plain dict with any of the keys feature_names, feature_types, cat_limit, target_name, scale_x_list, scale_y

  • omitted fields are auto-inferred from data (numpy heuristics) or synthesized (["x_0", ...], "y"); to start from a DataFrame use effector.from_dataframe
  • explicit fields always win over inference
None
random_state Optional[int]

seed for every internal random step (e.g. nof_instances subsampling)

  • use an int (default: 21), for reproducible output; two identical constructions give identical results
  • use None, for non-deterministic behavior
21

Methods:

Name Description
fit

Fit the ALE plot.

eval

Evaluate the mean effect of the feature-th feature at positions xs.

plot

Plot the (RH)ALE feature effect of feature feature.

Source code in effector/global_effect_ale.py
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
def __init__(
    self,
    data: np.ndarray,
    model: callable,
    *,
    nof_instances: Union[int, str] = 10_000,
    axis_limits: Optional[np.ndarray] = None,
    schema: Optional[Union[ingestion.Schema, dict]] = None,
    random_state: Optional[int] = 21,
):
    r"""
    Constructor for the ALE plot.

    Definition:
        ALE reveals the effect of $x_s$ by accumulating, bin by bin, the
        average *local effect* of the feature. The axis of $x_s$ is split
        into $K$ fixed bins by the limits $z_0 < z_1 < \dots < z_K$. For an
        instance $x^i$ whose $x_s^i$ falls in bin $k$, the local effect is
        the secant of the model across that bin:
        $$
        \mathtt{effect}_i = \frac{f(x^i_{s=z_k}) - f(x^i_{s=z_{k-1}})}{z_k - z_{k-1}}
        $$
        where $x^i_{s=z}$ is $x^i$ with its $s$-th coordinate set to $z$.
        The bin effect is the mean local effect over the instances $S_k$
        that fall in bin $k$, and ALE at a point $x$ lying in bin $k_x$
        accumulates the completed bins plus the partial contribution of the
        current one:
        $$
        \mu_k = \frac{1}{|S_k|} \sum_{i \in S_k} \mathtt{effect}_i
        \qquad
        \hat{f}^{ALE}(x) = \sum_{k=1}^{k_x - 1} (z_k - z_{k-1})\, \mu_k
                           + (x - z_{k_x - 1})\, \mu_{k_x}
        $$
        The curve is centered afterwards (by default `zero_integral`,
        subtracting its mean over the axis).

        The heterogeneity is the variance of the local effects within the
        bin containing $x$; `eval_heter` returns it as a step function:
        $$
        H(x) = \sigma^2_{k_x},
        \qquad
        \sigma^2_k = \frac{1}{|S_k|} \sum_{i \in S_k} (\mathtt{effect}_i - \mu_k)^2
        $$

        The std of the bin-effects is $\sqrt{\sigma^2_k}$, drawn as the
        error bars on the bin plot.

    Notes:
        - The required parameters are `data` and `model`. The rest are optional.

    Args:
        data: the design matrix

            - shape: `(N,D)`
        model: the black-box model. Must be a `Callable` with:

            - input: `ndarray` of shape `(N, D)`
            - output: `ndarray` of shape `(N, )`

        nof_instances: the number of instances to use for the explanation

            - use an `int`, to specify the number of instances
            - use `"all"`, to use all the instances

        axis_limits: The limits of the feature effect plot along each axis

            - use a `ndarray` of shape `(2, D)`, to specify them manually
            - use `None`, to be inferred from the data

        schema: input metadata (R10) — an `effector.Schema` or a plain `dict`
            with any of the keys `feature_names`, `feature_types`,
            `cat_limit`, `target_name`, `scale_x_list`, `scale_y`

            - omitted fields are auto-inferred from `data` (numpy
              heuristics) or synthesized (`["x_0", ...]`, `"y"`); to start
              from a DataFrame use `effector.from_dataframe`
            - explicit fields always win over inference

        random_state: seed for every internal random step (e.g. `nof_instances` subsampling)

            - use an `int` (default: `21`), for reproducible output; two identical constructions give identical results
            - use `None`, for non-deterministic behavior
    """
    super(ALE, self).__init__(
        data,
        model,
        nof_instances=nof_instances,
        axis_limits=axis_limits,
        schema=schema,
        random_state=random_state,
        method_name="ALE",
    )

fit(features='all', *, centering=True, points_for_centering=helpers.NOF_INTERNAL_POINTS, binning_method='fixed', order=None)

Fit the ALE plot.

Parameters:

Name Type Description Default
features Union[int, str, list]

the features to fit. If set to "all", all the features will be fitted.

'all'
binning_method Union[str, Fixed]
  • If set to "fixed", the ALE plot will be computed with the default values, which are 20 bins with at least 10 points per bin and the feature is considered as categorical if it has less than 15 unique values.
  • If you want to change the parameters of the method, you pass an instance of the class effector.axis_partitioning.Fixed with the desired parameters. For example: Fixed(nof_bins=20, min_points_per_bin=0)
'fixed'
centering Union[bool, str]

whether to compute the normalization constant for centering the plot:

  • False means no centering
  • True or zero_integral centers around the y axis.
  • zero_start starts the plot from y=0.
True
points_for_centering int

the number of points to use for centering the plot. Default is 30.

NOF_INTERNAL_POINTS
order Union[None, str, list]

level order for a categorical feature of interest

  • None (default): ascending encoded order — exact for ordinal features; for nominal features it is arbitrary-but- deterministic, and the accumulated curve's shape depends on it (the meaningful quantities are the adjacent-level differences — see docs/method_semantics.md)
  • "similarity": induce the order from the other features (KS-distance seriation, Molnar/iml)
  • a list of the levels: declare it explicitly (applies to exactly one categorical feature)

Changing order requires calling fit again; eval/plot reuse the fitted order.

None
Source code in effector/global_effect_ale.py
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
def fit(
    self,
    features: typing.Union[int, str, list] = "all",
    *,
    centering: typing.Union[bool, str] = True,
    points_for_centering: int = helpers.NOF_INTERNAL_POINTS,
    binning_method: typing.Union[str, ap.Fixed] = "fixed",
    order: typing.Union[None, str, list] = None,
) -> None:
    """Fit the ALE plot.

    Args:
        features: the features to fit. If set to "all", all the features will be fitted.

        binning_method:

            - If set to `"fixed"`, the ALE plot will be computed with the  default values, which are
            `20` bins with at least `10` points per bin and the feature is considered as categorical if it has
            less than `15` unique values.
            - If you want to change the parameters of the method, you pass an instance of the
            class `effector.axis_partitioning.Fixed` with the desired parameters.
            For example: `Fixed(nof_bins=20, min_points_per_bin=0)`

        centering: whether to compute the normalization constant for centering the plot:

            - `False` means no centering
            - `True` or `zero_integral` centers around the `y` axis.
            - `zero_start` starts the plot from `y=0`.

        points_for_centering: the number of points to use for centering the plot. Default is 30.

        order: level order for a *categorical* feature of interest

            - `None` (default): ascending encoded order — exact for
              ordinal features; for nominal features it is arbitrary-but-
              deterministic, and the accumulated curve's *shape* depends
              on it (the meaningful quantities are the adjacent-level
              differences — see docs/method_semantics.md)
            - `"similarity"`: induce the order from the other features
              (KS-distance seriation, Molnar/iml)
            - a list of the levels: declare it explicitly (applies to
              exactly one categorical feature)

            Changing `order` requires calling `fit` again; `eval`/`plot`
            reuse the fitted order.
    """
    if not (binning_method == "fixed" or isinstance(binning_method, ap.Fixed)):
        raise ValueError(
            f"Invalid binning_method: {binning_method!r}; ALE works only with "
            "the fixed binning method ('fixed' or an ap.Fixed instance)"
        )
    self._validate_order_arg(features, order)

    self._fit_loop(
        features,
        centering,
        points_for_centering,
        binning_method=binning_method,
        order=order,
    )

eval(feature, xs, centering=None, mask=None)

Evaluate the mean effect of the feature-th feature at positions xs.

Notes

This is the one evaluation method of every effect class (R1): it always returns the mean effect as a single (T,) array. Heterogeneity lives on its own surface — eval_heter(feature, xs) for the curve, heter_score(feature) for the scalar, and payload(feature) for the method's raw object.

Parameters:

Name Type Description Default
feature int

index of feature of interest

required
xs ndarray

the points along the s-th axis to evaluate the effect at

  • np.ndarray of shape (T, )
required
centering Union[None, bool, str]

whether to center the effect

  • None (default) uses the class default (DEFAULT_CENTERING)
  • False: no centering
  • True or "zero_integral": center around the y axis
  • "zero_start": the effect starts from y=0
None
mask Optional[ndarray]

optional boolean (N,) selecting a subregion. None (default) evaluates the fitted state; a mask summarizes that subset of the cached local effects on the fly — the effect within the subregion, on the global frame, without model calls. Centering is then computed over the subregion's own interval. Nothing is stored.

None

Returns:

Type Description
ndarray

the mean effect y at the given xs, (T,)

Source code in effector/global_effect.py
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
def eval(
    self,
    feature: int,
    xs: np.ndarray,
    centering: Union[None, bool, str] = None,
    mask: Optional[np.ndarray] = None,
) -> np.ndarray:
    """Evaluate the mean effect of the `feature`-th feature at positions `xs`.

    Notes:
        This is the one evaluation method of every effect class (R1): it
        always returns the mean effect as a single `(T,)` array.
        Heterogeneity lives on its own surface — `eval_heter(feature, xs)`
        for the curve, `heter_score(feature)` for the scalar, and
        `payload(feature)` for the method's raw object.

    Args:
        feature: index of feature of interest
        xs: the points along the s-th axis to evaluate the effect at

          - `np.ndarray` of shape `(T, )`

        centering: whether to center the effect

            - `None` (default) uses the class default (`DEFAULT_CENTERING`)
            - `False`: no centering
            - `True` or `"zero_integral"`: center around the `y` axis
            - `"zero_start"`: the effect starts from `y=0`

        mask: optional boolean `(N,)` selecting a subregion. `None`
            (default) evaluates the fitted state; a mask summarizes that
            subset of the cached local effects on the fly — the effect
            *within* the subregion, on the global frame, without model
            calls. Centering is then computed over the subregion's own
            interval. Nothing is stored.

    Returns:
        the mean effect `y` at the given `xs`, `(T,)`
    """
    centering = self.DEFAULT_CENTERING if centering is None else centering
    centering = helpers.prep_centering(centering)
    mask = self._prep_mask(mask)

    if mask is not None:
        if not self._is_cat(feature):
            self._effective_limits(feature, mask)  # degeneracy guard
        self._ensure_local_effects(feature)
        params = self._summarize(feature, mask, **self._replay_fit_kwargs(feature))
        y = self._eval_masked_mean(feature, xs, params, mask)
        if centering is not False:
            norm_const = self._compute_norm_const(
                feature, method=centering, params=params, mask=mask
            )
            y = y - self._mean_norm_const(norm_const)
        return y

    if self.requires_refit(feature, centering):
        self._refit(feature, centering)

    if not self.axis_limits[0, feature] < self.axis_limits[1, feature]:
        raise ValueError(
            f"Feature {feature} has a degenerate axis interval "
            f"[{self.axis_limits[0, feature]}, {self.axis_limits[1, feature]}]"
        )

    y = self._eval_unnorm(feature, xs)
    if centering is not False:
        norm_const = self.feature_effect["feature_" + str(feature)]["norm_const"]
        y = y - self._mean_norm_const(norm_const)
    return y

plot(feature, heterogeneity=True, centering=True, scale_x=None, scale_y=None, show_avg_output=False, y_limits=None, dy_limits=None, show_only_aggregated=False, show_plot=True, mask=None, feature_label=None)

Plot the (RH)ALE feature effect of feature feature.

Notes

This is a common method inherited by both ALE and RHALE.

Parameters:

Name Type Description Default
feature int

the feature to plot

required
heterogeneity Union[bool, str]

whether to plot the heterogeneity

  • False, plots only the mean effect
  • True or "std", the std of the bin-effects will be plotted using a red vertical bar
True
centering Union[bool, str]

whether to center the plot:

  • False means no centering
  • True or zero_integral centers around the y axis.
  • zero_start starts the plot from y=0.
True
scale_x Optional[dict]

None or Dict with keys ['std', 'mean']

  • If set to None, no scaling will be applied.
  • If set to a dict, the x-axis will be scaled by the standard deviation and the mean.
None
scale_y Optional[dict]

None or Dict with keys ['std', 'mean']

  • If set to None, no scaling will be applied.
  • If set to a dict, the y-axis will be scaled by the standard deviation and the mean.
None
show_avg_output bool

if True, the average output will be shown as a horizontal line.

False
y_limits Optional[List]

None or tuple, the limits of the y-axis

  • If set to None, the limits of the y-axis are set automatically
  • If set to a tuple, the limits are manually set
None
dy_limits Optional[List]

None or tuple, the limits of the dy-axis

  • If set to None, the limits of the dy-axis are set automatically
  • If set to a tuple, the limits are manually set
None
show_only_aggregated bool

if True, only the main ale plot will be shown

False
show_plot bool

if True, the plot will be shown

True
mask Optional[ndarray]

optional boolean (N,) selecting a subregion — plot the effect within it (re-binned from the cached local effects, no model calls) with the x-axis windowed to the subregion's own interval

None
feature_label Optional[str]

optional display name for the feature axis (e.g. a regional node's name), overriding feature_names[feature]

None
Source code in effector/global_effect_ale.py
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
def plot(
    self,
    feature: int,
    heterogeneity: Union[bool, str] = True,
    centering: Union[bool, str] = True,
    scale_x: Optional[dict] = None,
    scale_y: Optional[dict] = None,
    show_avg_output: bool = False,
    y_limits: Optional[List] = None,
    dy_limits: Optional[List] = None,
    show_only_aggregated: bool = False,
    show_plot: bool = True,
    mask: Optional[np.ndarray] = None,
    feature_label: Optional[str] = None,
):
    """
    Plot the (RH)ALE feature effect of feature `feature`.

    Notes:
        This is a common method inherited by both ALE and RHALE.

    Parameters:
        feature: the feature to plot
        heterogeneity: whether to plot the heterogeneity

              - `False`, plots only the mean effect
              - `True` or `"std"`, the std of the bin-effects will be plotted using a red vertical bar

        centering: whether to center the plot:

            - `False` means no centering
            - `True` or `zero_integral` centers around the `y` axis.
            - `zero_start` starts the plot from `y=0`.

        scale_x: None or Dict with keys ['std', 'mean']

            - If set to None, no scaling will be applied.
            - If set to a dict, the x-axis will be scaled by the standard deviation and the mean.
        scale_y: None or Dict with keys ['std', 'mean']

            - If set to None, no scaling will be applied.
            - If set to a dict, the y-axis will be scaled by the standard deviation and the mean.
        show_avg_output: if True, the average output will be shown as a horizontal line.
        y_limits: None or tuple, the limits of the y-axis

            - If set to None, the limits of the y-axis are set automatically
            - If set to a tuple, the limits are manually set

        dy_limits: None or tuple, the limits of the dy-axis

            - If set to None, the limits of the dy-axis are set automatically
            - If set to a tuple, the limits are manually set

        show_only_aggregated: if True, only the main ale plot will be shown
        show_plot: if True, the plot will be shown
        mask: optional boolean `(N,)` selecting a subregion — plot the
            effect *within* it (re-binned from the cached local effects,
            no model calls) with the x-axis windowed to the subregion's
            own interval
        feature_label: optional display name for the feature axis (e.g. a
            regional node's name), overriding `feature_names[feature]`
    """
    heterogeneity = helpers.prep_confidence_interval(heterogeneity)
    centering = helpers.prep_centering(centering)
    scale_x = helpers.resolve_scale(
        scale_x, self.scale_x_list[feature] if self.scale_x_list else None
    )
    scale_y = helpers.resolve_scale(scale_y, self.scale_y)
    mask = self._prep_mask(mask)
    feature_names = list(self.feature_names)
    if feature_label is not None:
        feature_names[feature] = feature_label

    if mask is None:
        # fit the feature if needed (the eval below reuses the stored state)
        self.eval(
            feature, np.array([self.axis_limits[0, feature]]), centering=centering
        )
        params = self.feature_effect["feature_" + str(feature)]
        x_window = None
    else:
        # transient subregion payload from the cached local effects —
        # pure numpy, nothing stored (the masked-plot path)
        self._ensure_local_effects(feature)
        params = self._summarize(feature, mask, **self._replay_fit_kwargs(feature))
        x_window = (
            None if params.get("is_cat") else self._effective_limits(feature, mask)
        )

    def centered_eval(xs):
        if mask is None:
            return self.eval(feature, xs, centering=centering)
        y = self._eval_unnorm(feature, xs, params=params)
        if centering is not False:
            y = y - self._compute_norm_const(
                feature, method=centering, params=params, mask=mask
            )
        return y

    # the accumulated curve is piecewise linear between bin limits, so
    # evaluating exactly at the limits draws it exactly (no resampling).
    # categoricals are drawn by the is_cat branch below (at their observed
    # level values); their limits are positional codes 0..K-1 that eval
    # would reject, so only build this grid for continuous features.
    if not params.get("is_cat"):
        x = np.asarray(params["limits"], dtype=float)
        y = centered_eval(x)

    if show_avg_output:
        data = self.data if mask is None else self.data[mask]
        avg_output = helpers.prep_avg_output(data, self.model, None, scale_y)
    else:
        avg_output = None

    title = (
        "Accumulated Local Effects (ALE)"
        if self.method_name == "ale"
        else "Robust and Heterogeneity-Aware ALE (RHALE)"
    )
    if params.get("is_cat"):
        # bars = accumulated per-level values (in fit order); whiskers =
        # the variance of the step into each level (method_semantics.md)
        levels, labels = self._level_display(feature, params["levels"])
        y_levels = centered_eval(levels)
        variances = (
            self._eval_unnorm(feature, levels, heterogeneity=True, params=params)[1]
            if heterogeneity is not False
            else None
        )
        positions = np.asarray(levels, dtype=float)
        if np.any(np.diff(positions) < 0):
            # custom (declared/induced) order: draw by rank, label by level
            if labels is None:
                labels = [f"{v:g}" for v in positions]
            positions = np.arange(len(positions), dtype=float)
        return vis.plot_categorical_effect(
            positions,
            y_levels,
            variances,
            feature,
            heterogeneity,
            title=title,
            level_labels=labels,
            scale_x=scale_x,
            scale_y=scale_y,
            avg_output=avg_output,
            feature_names=feature_names,
            target_name=self.target_name,
            y_limits=y_limits,
            connect_line=True,  # (RH)ALE bars accumulate: show the step path
            show_plot=show_plot,
        )
    return vis.ale_plot(
        x,
        y,
        bin_effect=params["bin_effect"],
        bin_variance=params["bin_variance"],
        limits=params["limits"],
        dx=params["dx"],
        feature=feature,
        heterogeneity=heterogeneity,
        scale_x=scale_x,
        scale_y=scale_y,
        title=title,
        avg_output=avg_output,
        feature_names=feature_names,
        target_name=self.target_name,
        y_limits=y_limits,
        dy_limits=dy_limits,
        show_only_aggregated=show_only_aggregated,
        show_plot=show_plot,
        x_limits=x_window,
    )

effector.global_effect_ale.RHALE(data, model, model_jac=None, *, data_effect=None, nof_instances=10000, axis_limits=None, schema=None, random_state=21)

Bases: ALEBase

Constructor for RHALE.

Definition

RHALE is ALE with the pointwise derivative as the local effect. Because the effect is read at the instance instead of as a secant across the bin, it no longer depends on the bin width, which makes the accumulated curve and the per-bin heterogeneity robust to the binning. The axis of \(x_s\) is split into \(K\) bins by the limits \(z_0 < z_1 < \dots < z_K\), and for an instance \(x^i\) whose \(x_s^i\) falls in bin \(k\) the local effect is $$ \mathtt{effect}i = \frac{\partial f}{\partial x_s}(x^i) $$ taken from the model Jacobian (exact if model_jac is provided, otherwise numerical). The bin effect, the accumulation and the heterogeneity are then identical to ALE: $$ \mu_k = \frac{1}{|S_k|} \sum} \mathtt{effecti \qquad \hat{f}^{RHALE}(x) = \sum)\, \mu_k + (x - z_{k_x - 1})\, \mu_{k_x} $$ The curve is centered afterwards (by default }^{k_x - 1} (z_k - z_{k-1zero_integral).

The heterogeneity is the variance of the local effects within the bin containing \(x\); eval_heter returns it as a step function: $$ H(x) = \sigma^2_{k_x}, \qquad \sigma^2_k = \frac{1}{|S_k|} \sum_{i \in S_k} (\mathtt{effect}_i - \mu_k)^2 $$

The std of the bin-effects is \(\sqrt{\sigma^2_k}\), drawn as the error bars on the bin plot.

Notes

The required parameters are data and model. The rest are optional.

Parameters:

Name Type Description Default
data ndarray

the design matrix

  • shape: (N,D)
required
model callable

the black-box model. Must be a Callable with:

  • input: ndarray of shape (N, D)
  • output: ndarray of shape (N, )
required
model_jac Union[None, callable]

the Jacobian of the model. Must be a Callable with:

  • input: ndarray of shape (N, D)
  • output: ndarray of shape (N, D)
None
nof_instances Union[int, str]

the number of instances to use for the explanation

  • use an int, to specify the number of instances
  • use "all", to use all the instances
10000
axis_limits Optional[ndarray]

The limits of the feature effect plot along each axis

  • use a ndarray of shape (2, D), to specify them manually
  • use None, to be inferred from the data
None
data_effect Optional[ndarray]
  • if np.ndarray, the model Jacobian computed on the data
  • if None, the Jacobian will be computed using model_jac
None
schema Optional[Union[Schema, dict]]

input metadata (R10) — an effector.Schema or a plain dict with any of the keys feature_names, feature_types, cat_limit, target_name, scale_x_list, scale_y

  • omitted fields are auto-inferred from data (numpy heuristics) or synthesized (["x_0", ...], "y"); to start from a DataFrame use effector.from_dataframe
  • explicit fields always win over inference
None
random_state Optional[int]

seed for every internal random step (e.g. nof_instances subsampling)

  • use an int (default: 21), for reproducible output; two identical constructions give identical results
  • use None, for non-deterministic behavior
21

Methods:

Name Description
fit

Fit the model.

eval

Evaluate the mean effect of the feature-th feature at positions xs.

plot

Plot the (RH)ALE feature effect of feature feature.

Source code in effector/global_effect_ale.py
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
def __init__(
    self,
    data: np.ndarray,
    model: callable,
    model_jac: typing.Union[None, callable] = None,
    *,
    data_effect: typing.Optional[np.ndarray] = None,
    nof_instances: typing.Union[int, str] = 10_000,
    axis_limits: typing.Optional[np.ndarray] = None,
    schema: Optional[Union[ingestion.Schema, dict]] = None,
    random_state: typing.Optional[int] = 21,
):
    r"""
    Constructor for RHALE.

    Definition:
        RHALE is ALE with the *pointwise derivative* as the local effect.
        Because the effect is read at the instance instead of as a secant
        across the bin, it no longer depends on the bin width, which makes
        the accumulated curve and the per-bin heterogeneity robust to the
        binning. The axis of $x_s$ is split into $K$ bins by the limits
        $z_0 < z_1 < \dots < z_K$, and for an instance $x^i$ whose $x_s^i$
        falls in bin $k$ the local effect is
        $$
        \mathtt{effect}_i = \frac{\partial f}{\partial x_s}(x^i)
        $$
        taken from the model Jacobian (exact if `model_jac` is provided,
        otherwise numerical). The bin effect, the accumulation and the
        heterogeneity are then identical to ALE:
        $$
        \mu_k = \frac{1}{|S_k|} \sum_{i \in S_k} \mathtt{effect}_i
        \qquad
        \hat{f}^{RHALE}(x) = \sum_{k=1}^{k_x - 1} (z_k - z_{k-1})\, \mu_k
                             + (x - z_{k_x - 1})\, \mu_{k_x}
        $$
        The curve is centered afterwards (by default `zero_integral`).

        The heterogeneity is the variance of the local effects within the
        bin containing $x$; `eval_heter` returns it as a step function:
        $$
        H(x) = \sigma^2_{k_x},
        \qquad
        \sigma^2_k = \frac{1}{|S_k|} \sum_{i \in S_k} (\mathtt{effect}_i - \mu_k)^2
        $$

        The std of the bin-effects is $\sqrt{\sigma^2_k}$, drawn as the
        error bars on the bin plot.

    Notes:
        The required parameters are `data` and `model`. The rest are optional.

    Args:
        data: the design matrix

            - shape: `(N,D)`
        model: the black-box model. Must be a `Callable` with:

            - input: `ndarray` of shape `(N, D)`
            - output: `ndarray` of shape `(N, )`

        model_jac: the Jacobian of the model. Must be a `Callable` with:

            - input: `ndarray` of shape `(N, D)`
            - output: `ndarray` of shape `(N, D)`

        nof_instances: the number of instances to use for the explanation

            - use an `int`, to specify the number of instances
            - use `"all"`, to use all the instances

        axis_limits: The limits of the feature effect plot along each axis

            - use a `ndarray` of shape `(2, D)`, to specify them manually
            - use `None`, to be inferred from the data

        data_effect:
            - if np.ndarray, the model Jacobian computed on the `data`
            - if None, the Jacobian will be computed using model_jac

        schema: input metadata (R10) — an `effector.Schema` or a plain `dict`
            with any of the keys `feature_names`, `feature_types`,
            `cat_limit`, `target_name`, `scale_x_list`, `scale_y`

            - omitted fields are auto-inferred from `data` (numpy
              heuristics) or synthesized (`["x_0", ...]`, `"y"`); to start
              from a DataFrame use `effector.from_dataframe`
            - explicit fields always win over inference

        random_state: seed for every internal random step (e.g. `nof_instances` subsampling)

            - use an `int` (default: `21`), for reproducible output; two identical constructions give identical results
            - use `None`, for non-deterministic behavior
    """
    super(RHALE, self).__init__(
        data,
        model,
        model_jac,
        data_effect=data_effect,
        nof_instances=nof_instances,
        axis_limits=axis_limits,
        schema=schema,
        random_state=random_state,
        method_name="RHALE",
    )

fit(features='all', *, centering=True, points_for_centering=helpers.NOF_INTERNAL_POINTS, binning_method='dp', order=None, binning_scope='global')

Fit the model.

Parameters:

Name Type Description Default
features (int, str, list)

the features to fit.

  • If set to "all", all the features will be fitted.
'all'
binning_method str

the binning method to use.

  • Use "greedy" for using the Greedy binning solution with the default parameters. For custom parameters initialize a axis_partitioning.Greedy object
  • Use "dp" for using a Dynamic Programming binning solution with the default parameters. For custom parameters initialize a axis_partitioning.DynamicProgramming object
  • Use "fixed" for using a Fixed binning solution with the default parameters. For custom parameters initialize a axis_partitioning.Fixed object
'dp'
centering Union[bool, str]

whether to compute the normalization constant for centering the plot:

  • False means no centering
  • True or zero_integral centers around the y axis
  • zero_start starts the plot from y=0
True
points_for_centering int

the number of points to use for centering the plot. Default is 30.

NOF_INTERNAL_POINTS
order Union[None, str, list]

level order for a categorical feature of interest

  • None (default): ascending encoded order — exact for ordinal features; for nominal features it is arbitrary-but- deterministic, and the accumulated curve's shape depends on it (the meaningful quantities are the adjacent-level differences — see docs/method_semantics.md)
  • "similarity": induce the order from the other features (KS-distance seriation, Molnar/iml)
  • a list of the levels: declare it explicitly (applies to exactly one categorical feature)

Changing order requires calling fit again; eval/plot reuse the fitted order.

None
binning_scope str

the x-range the binner covers when a masked summary re-bins a subregion (eval/eval_heter/plot/ heter_score with mask=; the regional split search)

  • "global" (default): the frozen global axis_limits — one frame for every subregion, directly comparable
  • "effective": the masked column's own [min, max] — bins packed into the subregion, finer resolution

Recorded at fit and replayed by every masked call, so the split search and the display always share the same scope. Ignored when no mask is involved.

'global'
Source code in effector/global_effect_ale.py
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
def fit(
    self,
    features: typing.Union[int, str, list] = "all",
    *,
    centering: typing.Union[bool, str] = True,
    points_for_centering: int = helpers.NOF_INTERNAL_POINTS,
    binning_method: typing.Union[
        str, ap.DynamicProgramming, ap.Agglomerative, ap.Quantile, ap.Fixed
    ] = "dp",
    order: typing.Union[None, str, list] = None,
    binning_scope: str = "global",
) -> None:
    """Fit the model.

    Args:
        features (int, str, list): the features to fit.

            - If set to "all", all the features will be fitted.

        binning_method (str): the binning method to use.

            - Use `"greedy"` for using the Greedy binning solution with the default parameters.
              For custom parameters initialize a `axis_partitioning.Greedy` object
            - Use `"dp"` for using a Dynamic Programming binning solution with the default parameters.
              For custom parameters initialize a `axis_partitioning.DynamicProgramming` object
            - Use `"fixed"` for using a Fixed binning solution with the default parameters.
              For custom parameters initialize a `axis_partitioning.Fixed` object

        centering: whether to compute the normalization constant for centering the plot:

            - `False` means no centering
            - `True` or `zero_integral` centers around the `y` axis
            - `zero_start` starts the plot from `y=0`

        points_for_centering: the number of points to use for centering the plot. Default is 30.

        order: level order for a *categorical* feature of interest

            - `None` (default): ascending encoded order — exact for
              ordinal features; for nominal features it is arbitrary-but-
              deterministic, and the accumulated curve's *shape* depends
              on it (the meaningful quantities are the adjacent-level
              differences — see docs/method_semantics.md)
            - `"similarity"`: induce the order from the other features
              (KS-distance seriation, Molnar/iml)
            - a list of the levels: declare it explicitly (applies to
              exactly one categorical feature)

            Changing `order` requires calling `fit` again; `eval`/`plot`
            reuse the fitted order.

        binning_scope: the x-range the binner covers when a *masked*
            summary re-bins a subregion (`eval`/`eval_heter`/`plot`/
            `heter_score` with `mask=`; the regional split search)

            - `"global"` (default): the frozen global `axis_limits` — one
              frame for every subregion, directly comparable
            - `"effective"`: the masked column's own `[min, max]` — bins
              packed into the subregion, finer resolution

            Recorded at fit and replayed by every masked call, so the
            split search and the display always share the same scope.
            Ignored when no mask is involved.
    """
    # validation is the resolver's job (R6): one table, one error message
    binning_method = ap.return_default(binning_method)
    self._validate_order_arg(features, order)
    check_binning_scope(binning_scope)

    self._fit_loop(
        features,
        centering,
        points_for_centering,
        binning_method=binning_method,
        order=order,
        binning_scope=binning_scope,
    )

eval(feature, xs, centering=None, mask=None)

Evaluate the mean effect of the feature-th feature at positions xs.

Notes

This is the one evaluation method of every effect class (R1): it always returns the mean effect as a single (T,) array. Heterogeneity lives on its own surface — eval_heter(feature, xs) for the curve, heter_score(feature) for the scalar, and payload(feature) for the method's raw object.

Parameters:

Name Type Description Default
feature int

index of feature of interest

required
xs ndarray

the points along the s-th axis to evaluate the effect at

  • np.ndarray of shape (T, )
required
centering Union[None, bool, str]

whether to center the effect

  • None (default) uses the class default (DEFAULT_CENTERING)
  • False: no centering
  • True or "zero_integral": center around the y axis
  • "zero_start": the effect starts from y=0
None
mask Optional[ndarray]

optional boolean (N,) selecting a subregion. None (default) evaluates the fitted state; a mask summarizes that subset of the cached local effects on the fly — the effect within the subregion, on the global frame, without model calls. Centering is then computed over the subregion's own interval. Nothing is stored.

None

Returns:

Type Description
ndarray

the mean effect y at the given xs, (T,)

Source code in effector/global_effect.py
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
def eval(
    self,
    feature: int,
    xs: np.ndarray,
    centering: Union[None, bool, str] = None,
    mask: Optional[np.ndarray] = None,
) -> np.ndarray:
    """Evaluate the mean effect of the `feature`-th feature at positions `xs`.

    Notes:
        This is the one evaluation method of every effect class (R1): it
        always returns the mean effect as a single `(T,)` array.
        Heterogeneity lives on its own surface — `eval_heter(feature, xs)`
        for the curve, `heter_score(feature)` for the scalar, and
        `payload(feature)` for the method's raw object.

    Args:
        feature: index of feature of interest
        xs: the points along the s-th axis to evaluate the effect at

          - `np.ndarray` of shape `(T, )`

        centering: whether to center the effect

            - `None` (default) uses the class default (`DEFAULT_CENTERING`)
            - `False`: no centering
            - `True` or `"zero_integral"`: center around the `y` axis
            - `"zero_start"`: the effect starts from `y=0`

        mask: optional boolean `(N,)` selecting a subregion. `None`
            (default) evaluates the fitted state; a mask summarizes that
            subset of the cached local effects on the fly — the effect
            *within* the subregion, on the global frame, without model
            calls. Centering is then computed over the subregion's own
            interval. Nothing is stored.

    Returns:
        the mean effect `y` at the given `xs`, `(T,)`
    """
    centering = self.DEFAULT_CENTERING if centering is None else centering
    centering = helpers.prep_centering(centering)
    mask = self._prep_mask(mask)

    if mask is not None:
        if not self._is_cat(feature):
            self._effective_limits(feature, mask)  # degeneracy guard
        self._ensure_local_effects(feature)
        params = self._summarize(feature, mask, **self._replay_fit_kwargs(feature))
        y = self._eval_masked_mean(feature, xs, params, mask)
        if centering is not False:
            norm_const = self._compute_norm_const(
                feature, method=centering, params=params, mask=mask
            )
            y = y - self._mean_norm_const(norm_const)
        return y

    if self.requires_refit(feature, centering):
        self._refit(feature, centering)

    if not self.axis_limits[0, feature] < self.axis_limits[1, feature]:
        raise ValueError(
            f"Feature {feature} has a degenerate axis interval "
            f"[{self.axis_limits[0, feature]}, {self.axis_limits[1, feature]}]"
        )

    y = self._eval_unnorm(feature, xs)
    if centering is not False:
        norm_const = self.feature_effect["feature_" + str(feature)]["norm_const"]
        y = y - self._mean_norm_const(norm_const)
    return y

plot(feature, heterogeneity=True, centering=True, scale_x=None, scale_y=None, show_avg_output=False, y_limits=None, dy_limits=None, show_only_aggregated=False, show_plot=True, mask=None, feature_label=None)

Plot the (RH)ALE feature effect of feature feature.

Notes

This is a common method inherited by both ALE and RHALE.

Parameters:

Name Type Description Default
feature int

the feature to plot

required
heterogeneity Union[bool, str]

whether to plot the heterogeneity

  • False, plots only the mean effect
  • True or "std", the std of the bin-effects will be plotted using a red vertical bar
True
centering Union[bool, str]

whether to center the plot:

  • False means no centering
  • True or zero_integral centers around the y axis.
  • zero_start starts the plot from y=0.
True
scale_x Optional[dict]

None or Dict with keys ['std', 'mean']

  • If set to None, no scaling will be applied.
  • If set to a dict, the x-axis will be scaled by the standard deviation and the mean.
None
scale_y Optional[dict]

None or Dict with keys ['std', 'mean']

  • If set to None, no scaling will be applied.
  • If set to a dict, the y-axis will be scaled by the standard deviation and the mean.
None
show_avg_output bool

if True, the average output will be shown as a horizontal line.

False
y_limits Optional[List]

None or tuple, the limits of the y-axis

  • If set to None, the limits of the y-axis are set automatically
  • If set to a tuple, the limits are manually set
None
dy_limits Optional[List]

None or tuple, the limits of the dy-axis

  • If set to None, the limits of the dy-axis are set automatically
  • If set to a tuple, the limits are manually set
None
show_only_aggregated bool

if True, only the main ale plot will be shown

False
show_plot bool

if True, the plot will be shown

True
mask Optional[ndarray]

optional boolean (N,) selecting a subregion — plot the effect within it (re-binned from the cached local effects, no model calls) with the x-axis windowed to the subregion's own interval

None
feature_label Optional[str]

optional display name for the feature axis (e.g. a regional node's name), overriding feature_names[feature]

None
Source code in effector/global_effect_ale.py
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
def plot(
    self,
    feature: int,
    heterogeneity: Union[bool, str] = True,
    centering: Union[bool, str] = True,
    scale_x: Optional[dict] = None,
    scale_y: Optional[dict] = None,
    show_avg_output: bool = False,
    y_limits: Optional[List] = None,
    dy_limits: Optional[List] = None,
    show_only_aggregated: bool = False,
    show_plot: bool = True,
    mask: Optional[np.ndarray] = None,
    feature_label: Optional[str] = None,
):
    """
    Plot the (RH)ALE feature effect of feature `feature`.

    Notes:
        This is a common method inherited by both ALE and RHALE.

    Parameters:
        feature: the feature to plot
        heterogeneity: whether to plot the heterogeneity

              - `False`, plots only the mean effect
              - `True` or `"std"`, the std of the bin-effects will be plotted using a red vertical bar

        centering: whether to center the plot:

            - `False` means no centering
            - `True` or `zero_integral` centers around the `y` axis.
            - `zero_start` starts the plot from `y=0`.

        scale_x: None or Dict with keys ['std', 'mean']

            - If set to None, no scaling will be applied.
            - If set to a dict, the x-axis will be scaled by the standard deviation and the mean.
        scale_y: None or Dict with keys ['std', 'mean']

            - If set to None, no scaling will be applied.
            - If set to a dict, the y-axis will be scaled by the standard deviation and the mean.
        show_avg_output: if True, the average output will be shown as a horizontal line.
        y_limits: None or tuple, the limits of the y-axis

            - If set to None, the limits of the y-axis are set automatically
            - If set to a tuple, the limits are manually set

        dy_limits: None or tuple, the limits of the dy-axis

            - If set to None, the limits of the dy-axis are set automatically
            - If set to a tuple, the limits are manually set

        show_only_aggregated: if True, only the main ale plot will be shown
        show_plot: if True, the plot will be shown
        mask: optional boolean `(N,)` selecting a subregion — plot the
            effect *within* it (re-binned from the cached local effects,
            no model calls) with the x-axis windowed to the subregion's
            own interval
        feature_label: optional display name for the feature axis (e.g. a
            regional node's name), overriding `feature_names[feature]`
    """
    heterogeneity = helpers.prep_confidence_interval(heterogeneity)
    centering = helpers.prep_centering(centering)
    scale_x = helpers.resolve_scale(
        scale_x, self.scale_x_list[feature] if self.scale_x_list else None
    )
    scale_y = helpers.resolve_scale(scale_y, self.scale_y)
    mask = self._prep_mask(mask)
    feature_names = list(self.feature_names)
    if feature_label is not None:
        feature_names[feature] = feature_label

    if mask is None:
        # fit the feature if needed (the eval below reuses the stored state)
        self.eval(
            feature, np.array([self.axis_limits[0, feature]]), centering=centering
        )
        params = self.feature_effect["feature_" + str(feature)]
        x_window = None
    else:
        # transient subregion payload from the cached local effects —
        # pure numpy, nothing stored (the masked-plot path)
        self._ensure_local_effects(feature)
        params = self._summarize(feature, mask, **self._replay_fit_kwargs(feature))
        x_window = (
            None if params.get("is_cat") else self._effective_limits(feature, mask)
        )

    def centered_eval(xs):
        if mask is None:
            return self.eval(feature, xs, centering=centering)
        y = self._eval_unnorm(feature, xs, params=params)
        if centering is not False:
            y = y - self._compute_norm_const(
                feature, method=centering, params=params, mask=mask
            )
        return y

    # the accumulated curve is piecewise linear between bin limits, so
    # evaluating exactly at the limits draws it exactly (no resampling).
    # categoricals are drawn by the is_cat branch below (at their observed
    # level values); their limits are positional codes 0..K-1 that eval
    # would reject, so only build this grid for continuous features.
    if not params.get("is_cat"):
        x = np.asarray(params["limits"], dtype=float)
        y = centered_eval(x)

    if show_avg_output:
        data = self.data if mask is None else self.data[mask]
        avg_output = helpers.prep_avg_output(data, self.model, None, scale_y)
    else:
        avg_output = None

    title = (
        "Accumulated Local Effects (ALE)"
        if self.method_name == "ale"
        else "Robust and Heterogeneity-Aware ALE (RHALE)"
    )
    if params.get("is_cat"):
        # bars = accumulated per-level values (in fit order); whiskers =
        # the variance of the step into each level (method_semantics.md)
        levels, labels = self._level_display(feature, params["levels"])
        y_levels = centered_eval(levels)
        variances = (
            self._eval_unnorm(feature, levels, heterogeneity=True, params=params)[1]
            if heterogeneity is not False
            else None
        )
        positions = np.asarray(levels, dtype=float)
        if np.any(np.diff(positions) < 0):
            # custom (declared/induced) order: draw by rank, label by level
            if labels is None:
                labels = [f"{v:g}" for v in positions]
            positions = np.arange(len(positions), dtype=float)
        return vis.plot_categorical_effect(
            positions,
            y_levels,
            variances,
            feature,
            heterogeneity,
            title=title,
            level_labels=labels,
            scale_x=scale_x,
            scale_y=scale_y,
            avg_output=avg_output,
            feature_names=feature_names,
            target_name=self.target_name,
            y_limits=y_limits,
            connect_line=True,  # (RH)ALE bars accumulate: show the step path
            show_plot=show_plot,
        )
    return vis.ale_plot(
        x,
        y,
        bin_effect=params["bin_effect"],
        bin_variance=params["bin_variance"],
        limits=params["limits"],
        dx=params["dx"],
        feature=feature,
        heterogeneity=heterogeneity,
        scale_x=scale_x,
        scale_y=scale_y,
        title=title,
        avg_output=avg_output,
        feature_names=feature_names,
        target_name=self.target_name,
        y_limits=y_limits,
        dy_limits=dy_limits,
        show_only_aggregated=show_only_aggregated,
        show_plot=show_plot,
        x_limits=x_window,
    )

effector.global_effect_pdp.PDP(data, model, *, axis_limits=None, nof_instances=10000, schema=None, random_state=21)

Bases: PDPBase

Constructor of the PDP class.

Definition

PDP: $$ PDP(x_s) = {1 \over N} \sum_{i=1}^N f(x_s, \mathbf{x}_c^i) $$

centered-PDP: $$ PDP_c(x_s) = PDP(x_s) - c, \quad c = {1 \over M} \sum_{j=1}^M PDP(x_s^j) $$

ICE: $$ ICE^i(x_s) = f(x_s, \mathbf{x}_c^i), \quad i=1, \dots, N $$

centered-ICE: $$ ICE_c^i(x_s) = ICE^i(x_s) - c_i, \quad c_i = {1 \over M} \sum_{j=1}^M ICE^i(x_s^j) $$

heterogeneity function: $$ h(x_s) = {1 \over N} \sum_{i=1}^N ( ICE_c^i(x_s) - PDP_c(x_s) )^2 $$

The heterogeneity value is: $$ \mathcal{H}(x_s) = {1 \over M} \sum_{j=1}^M h(x_s^j), $$ where \(x_s^j\) are an equally spaced grid of points in \([x_s^{\min}, x_s^{\max}]\).

Notes

The required parameters are data and model. The rest are optional.

Parameters:

Name Type Description Default
data ndarray

the design matrix

  • shape: (N,D)
required
model Callable

the black-box model. Must be a Callable with:

  • input: ndarray of shape (N, D)
  • output: ndarray of shape (N,)
required
axis_limits Optional[ndarray]

The limits of the feature effect plot along each axis

  • use a ndarray of shape (2, D), to specify them manually
  • use None, to be inferred from the data
None
nof_instances Union[int, str]

maximum number of instances to be used

  • use "all", for using all instances.
  • use an int, for selecting nof_instances instances randomly.
10000
schema Optional[Union[Schema, dict]]

input metadata (R10) — an effector.Schema or a plain dict with any of the keys feature_names, feature_types, cat_limit, target_name, scale_x_list, scale_y

  • omitted fields are auto-inferred from data (numpy heuristics) or synthesized (["x_0", ...], "y"); to start from a DataFrame use effector.from_dataframe
  • explicit fields always win over inference
None
random_state Optional[int]

seed for every internal random step (e.g. nof_instances subsampling)

  • use an int (default: 21), for reproducible output; two identical constructions give identical results
  • use None, for non-deterministic behavior
21

Methods:

Name Description
fit

Fit the Feature effect to the data.

eval

Evaluate the mean effect of the feature-th feature at positions xs.

plot

Plot the feature effect.

Source code in effector/global_effect_pdp.py
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
def __init__(
    self,
    data: np.ndarray,
    model: Callable,
    *,
    axis_limits: Optional[np.ndarray] = None,
    nof_instances: Union[int, str] = 10_000,
    schema: Optional[Union[ingestion.Schema, dict]] = None,
    random_state: Optional[int] = 21,
):
    r"""
    Constructor of the PDP class.

    Definition:
        PDP:
        $$
        PDP(x_s) = {1 \over N} \sum_{i=1}^N f(x_s, \mathbf{x}_c^i)
        $$

        centered-PDP:
        $$
        PDP_c(x_s) = PDP(x_s) - c, \quad c = {1 \over M} \sum_{j=1}^M PDP(x_s^j)
        $$

        ICE:
        $$
        ICE^i(x_s) = f(x_s, \mathbf{x}_c^i), \quad i=1, \dots, N
        $$

        centered-ICE:
        $$
        ICE_c^i(x_s) = ICE^i(x_s) - c_i, \quad c_i = {1 \over M} \sum_{j=1}^M ICE^i(x_s^j)
        $$

        heterogeneity function:
        $$
        h(x_s) = {1 \over N} \sum_{i=1}^N ( ICE_c^i(x_s) - PDP_c(x_s) )^2
        $$

        The heterogeneity value is:
        $$
        \mathcal{H}(x_s) = {1 \over M} \sum_{j=1}^M h(x_s^j),
        $$
        where $x_s^j$ are an equally spaced grid of points in $[x_s^{\min}, x_s^{\max}]$.

    Notes:
        The required parameters are `data` and `model`. The rest are optional.

    Args:
        data: the design matrix

            - shape: `(N,D)`
        model: the black-box model. Must be a `Callable` with:

            - input: `ndarray` of shape `(N, D)`
            - output: `ndarray` of shape `(N,)`

        axis_limits: The limits of the feature effect plot along each axis

            - use a `ndarray` of shape `(2, D)`, to specify them manually
            - use `None`, to be inferred from the data

        nof_instances: maximum number of instances to be used

            - use "all", for using all instances.
            - use an `int`, for selecting `nof_instances` instances randomly.

        schema: input metadata (R10) — an `effector.Schema` or a plain `dict`
            with any of the keys `feature_names`, `feature_types`,
            `cat_limit`, `target_name`, `scale_x_list`, `scale_y`

            - omitted fields are auto-inferred from `data` (numpy
              heuristics) or synthesized (`["x_0", ...]`, `"y"`); to start
              from a DataFrame use `effector.from_dataframe`
            - explicit fields always win over inference

        random_state: seed for every internal random step (e.g. `nof_instances` subsampling)

            - use an `int` (default: `21`), for reproducible output; two identical constructions give identical results
            - use `None`, for non-deterministic behavior
    """

    super(PDP, self).__init__(
        data,
        model,
        None,
        axis_limits=axis_limits,
        nof_instances=nof_instances,
        schema=schema,
        random_state=random_state,
        method_name="PDP",
    )

fit(features='all', *, centering=False, points_for_centering=helpers.NOF_INTERNAL_POINTS, use_vectorized=True)

Fit the Feature effect to the data.

Notes

You can use .eval or .plot without calling .fit explicitly. The only thing that .fit does is to compute the normalization constant for centering the PDP and ICE plots. This will be automatically done when calling eval or plot, so there is no need to call fit explicitly.

Parameters:

Name Type Description Default
features Union[int, str, list]

the features to fit. - If set to "all", all the features will be fitted.

'all'
centering Union[bool, str]

whether to center the plot:

  • False means no centering
  • True or zero_integral centers around the y axis.
  • zero_start starts the plot from y=0.
False
points_for_centering int

number of linspaced points along the feature axis used for centering.

NOF_INTERNAL_POINTS
use_vectorized bool

whether to use vectorized operations for the PDP and ICE curves

True
Source code in effector/global_effect_pdp.py
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
def fit(
    self,
    features: Union[int, str, list] = "all",
    *,
    centering: Union[bool, str] = False,
    points_for_centering: int = helpers.NOF_INTERNAL_POINTS,
    use_vectorized: bool = True,
):
    """
    Fit the Feature effect to the data.

    Notes:
        You can use `.eval` or `.plot` without calling `.fit` explicitly.
        The only thing that `.fit` does is to compute the normalization constant for centering the PDP and ICE plots.
        This will be automatically done when calling `eval` or `plot`, so there is no need to call `fit` explicitly.

    Args:
        features: the features to fit.
            - If set to "all", all the features will be fitted.

        centering: whether to center the plot:

            - `False` means no centering
            - `True` or `zero_integral` centers around the `y` axis.
            - `zero_start` starts the plot from `y=0`.

        points_for_centering: number of linspaced points along the feature axis used for centering.
        use_vectorized: whether to use vectorized operations for the PDP and ICE curves

    """
    self._fit_loop(
        features, centering, points_for_centering, use_vectorized=use_vectorized
    )

eval(feature, xs, centering=None, mask=None)

Evaluate the mean effect of the feature-th feature at positions xs.

Notes

This is the one evaluation method of every effect class (R1): it always returns the mean effect as a single (T,) array. Heterogeneity lives on its own surface — eval_heter(feature, xs) for the curve, heter_score(feature) for the scalar, and payload(feature) for the method's raw object.

Parameters:

Name Type Description Default
feature int

index of feature of interest

required
xs ndarray

the points along the s-th axis to evaluate the effect at

  • np.ndarray of shape (T, )
required
centering Union[None, bool, str]

whether to center the effect

  • None (default) uses the class default (DEFAULT_CENTERING)
  • False: no centering
  • True or "zero_integral": center around the y axis
  • "zero_start": the effect starts from y=0
None
mask Optional[ndarray]

optional boolean (N,) selecting a subregion. None (default) evaluates the fitted state; a mask summarizes that subset of the cached local effects on the fly — the effect within the subregion, on the global frame, without model calls. Centering is then computed over the subregion's own interval. Nothing is stored.

None

Returns:

Type Description
ndarray

the mean effect y at the given xs, (T,)

Source code in effector/global_effect.py
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
def eval(
    self,
    feature: int,
    xs: np.ndarray,
    centering: Union[None, bool, str] = None,
    mask: Optional[np.ndarray] = None,
) -> np.ndarray:
    """Evaluate the mean effect of the `feature`-th feature at positions `xs`.

    Notes:
        This is the one evaluation method of every effect class (R1): it
        always returns the mean effect as a single `(T,)` array.
        Heterogeneity lives on its own surface — `eval_heter(feature, xs)`
        for the curve, `heter_score(feature)` for the scalar, and
        `payload(feature)` for the method's raw object.

    Args:
        feature: index of feature of interest
        xs: the points along the s-th axis to evaluate the effect at

          - `np.ndarray` of shape `(T, )`

        centering: whether to center the effect

            - `None` (default) uses the class default (`DEFAULT_CENTERING`)
            - `False`: no centering
            - `True` or `"zero_integral"`: center around the `y` axis
            - `"zero_start"`: the effect starts from `y=0`

        mask: optional boolean `(N,)` selecting a subregion. `None`
            (default) evaluates the fitted state; a mask summarizes that
            subset of the cached local effects on the fly — the effect
            *within* the subregion, on the global frame, without model
            calls. Centering is then computed over the subregion's own
            interval. Nothing is stored.

    Returns:
        the mean effect `y` at the given `xs`, `(T,)`
    """
    centering = self.DEFAULT_CENTERING if centering is None else centering
    centering = helpers.prep_centering(centering)
    mask = self._prep_mask(mask)

    if mask is not None:
        if not self._is_cat(feature):
            self._effective_limits(feature, mask)  # degeneracy guard
        self._ensure_local_effects(feature)
        params = self._summarize(feature, mask, **self._replay_fit_kwargs(feature))
        y = self._eval_masked_mean(feature, xs, params, mask)
        if centering is not False:
            norm_const = self._compute_norm_const(
                feature, method=centering, params=params, mask=mask
            )
            y = y - self._mean_norm_const(norm_const)
        return y

    if self.requires_refit(feature, centering):
        self._refit(feature, centering)

    if not self.axis_limits[0, feature] < self.axis_limits[1, feature]:
        raise ValueError(
            f"Feature {feature} has a degenerate axis interval "
            f"[{self.axis_limits[0, feature]}, {self.axis_limits[1, feature]}]"
        )

    y = self._eval_unnorm(feature, xs)
    if centering is not False:
        norm_const = self.feature_effect["feature_" + str(feature)]["norm_const"]
        y = y - self._mean_norm_const(norm_const)
    return y

plot(feature, heterogeneity='ice', centering=True, nof_points=100, scale_x=None, scale_y=None, nof_ice=100, show_avg_output=False, y_limits=None, use_vectorized=True, show_plot=True, mask=None, feature_label=None)

Plot the feature effect.

Parameters:

Name Type Description Default
feature int

the feature to plot

required
heterogeneity Union[bool, str]

whether to plot the heterogeneity

  • False, plot only the mean effect
  • True or std, plot the standard deviation of the ICE curves
  • ice, also plot the ICE curves
'ice'
centering Union[bool, str]

whether to center the plot

  • False means no centering
  • True or zero_integral centers around the y axis.
  • zero_start starts the plot from y=0.
True
nof_points int

the grid size for the PDP plot

100
scale_x Optional[dict]

None or Dict with keys ['std', 'mean']

  • If set to None, no scaling will be applied.
  • If set to a dict, the x-axis will be scaled x = (x + mean) * std
None
scale_y Optional[dict]

None or Dict with keys ['std', 'mean']

  • If set to None, no scaling will be applied.
  • If set to a dict, the y-axis will be scaled y = (y + mean) * std
None
nof_ice Union[int, str]

number of ICE plots to show on top of the SHAP curve

100
show_avg_output bool

whether to show the average output of the model

False
y_limits Optional[List]

None or tuple, the limits of the y-axis

  • If set to None, the limits of the y-axis are set automatically
  • If set to a tuple, the limits are manually set
None
use_vectorized bool

whether to use the vectorized version of the PDP computation

True
mask Optional[ndarray]

optional boolean (N,) selecting a subregion — plot the PDP/ ICE within it from the cached ICE table (grid resolution; nof_points does not apply), model-free, with the x-axis windowed to the subregion's own interval

None
feature_label Optional[str]

optional display name for the feature axis (e.g. a regional node's name), overriding feature_names[feature]

None
Source code in effector/global_effect_pdp.py
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
def plot(
    self,
    feature: int,
    heterogeneity: Union[bool, str] = "ice",
    centering: Union[bool, str] = True,
    nof_points: int = 100,
    scale_x: Optional[dict] = None,
    scale_y: Optional[dict] = None,
    nof_ice: Union[int, str] = 100,
    show_avg_output: bool = False,
    y_limits: Optional[List] = None,
    use_vectorized: bool = True,
    show_plot: bool = True,
    mask: Optional[np.ndarray] = None,
    feature_label: Optional[str] = None,
):
    """
    Plot the feature effect.

    Parameters:
        feature: the feature to plot
        heterogeneity: whether to plot the heterogeneity

              - `False`, plot only the mean effect
              - `True` or `std`, plot the standard deviation of the ICE curves
              - `ice`, also plot the ICE curves

        centering: whether to center the plot

            - `False` means no centering
            - `True` or `zero_integral` centers around the `y` axis.
            - `zero_start` starts the plot from `y=0`.

        nof_points: the grid size for the PDP plot

        scale_x: None or Dict with keys ['std', 'mean']

            - If set to None, no scaling will be applied.
            - If set to a dict, the x-axis will be scaled `x = (x + mean) * std`

        scale_y: None or Dict with keys ['std', 'mean']

            - If set to None, no scaling will be applied.
            - If set to a dict, the y-axis will be scaled `y = (y + mean) * std`

        nof_ice: number of ICE plots to show on top of the SHAP curve
        show_avg_output: whether to show the average output of the model

        y_limits: None or tuple, the limits of the y-axis

            - If set to None, the limits of the y-axis are set automatically
            - If set to a tuple, the limits are manually set

        use_vectorized: whether to use the vectorized version of the PDP computation
        mask: optional boolean `(N,)` selecting a subregion — plot the PDP/
            ICE *within* it from the cached ICE table (grid resolution;
            `nof_points` does not apply), model-free, with the x-axis
            windowed to the subregion's own interval
        feature_label: optional display name for the feature axis (e.g. a
            regional node's name), overriding `feature_names[feature]`
    """
    ret = self._plot(
        feature,
        heterogeneity,
        centering,
        nof_points,
        scale_x,
        scale_y,
        nof_ice,
        show_avg_output,
        y_limits,
        use_vectorized,
        show_plot,
        mask,
        feature_label,
    )

    if not show_plot:
        return ret

effector.global_effect_pdp.DerPDP(data, model, model_jac=None, *, axis_limits=None, nof_instances=10000, schema=None, random_state=21)

Bases: PDPBase

Constructor of the DerivativePDP class.

Definition

d-PDP: $$ dPDP(x_s) = {1 \over N} \sum_{i=1}^N {\partial f \over \partial x_s}(x_s, \mathbf{x}_c^i) $$

centered-PDP: $$ dPDP_c(x_s) = dPDP(x_s) - c, \quad c = {1 \over M} \sum_{j=1}^M dPDP(x_s^j) $$

ICE: $$ dICE^i(x_s) = {\partial f \over \partial x_s}(x_s, \mathbf{x}_c^i), \quad i=1, \dots, N $$

centered-ICE: $$ dICE_c^i(x_s) = dICE^i(x_s) - c_i, \quad c_i = {1 \over M} \sum_{j=1}^M dICE^i(x_s^j) $$

heterogeneity function: $$ h(x_s) = {1 \over N} \sum_{i=1}^N ( dICE_c^i(x_s) - dPDP_c(x_s) )^2 $$

The heterogeneity value is: $$ \mathcal{H}(x_s) = {1 \over M} \sum_{j=1}^M h(x_s^j), $$ where \(x_s^j\) are an equally spaced grid of points in \([x_s^{\min}, x_s^{\max}]\).

Notes
  • The required parameters are data and model. The rest are optional.
  • The model_jac is the Jacobian of the model. If None, the Jacobian will be computed numerically.

Parameters:

Name Type Description Default
data ndarray

the design matrix

  • shape: (N,D)
required
model Callable

the black-box model. Must be a Callable with:

  • input: ndarray of shape (N, D)
  • output: ndarray of shape (N, )
required
model_jac Optional[Callable]

the black-box model Jacobian. Must be a Callable with:

  • input: ndarray of shape (N, D)
  • output: ndarray of shape (N, D)
None
axis_limits Optional[ndarray]

The limits of the feature effect plot along each axis

  • use a ndarray of shape (2, D), to specify them manually
  • use None, to be inferred from the data
None
nof_instances Union[int, str]

maximum number of instances to be used for PDP.

  • use "all", for using all instances.
  • use an int, for using nof_instances instances.
10000
schema Optional[Union[Schema, dict]]

input metadata (R10) — an effector.Schema or a plain dict with any of the keys feature_names, feature_types, cat_limit, target_name, scale_x_list, scale_y

  • omitted fields are auto-inferred from data (numpy heuristics) or synthesized (["x_0", ...], "y"); to start from a DataFrame use effector.from_dataframe
  • explicit fields always win over inference
None
random_state Optional[int]

seed for every internal random step (e.g. nof_instances subsampling)

  • use an int (default: 21), for reproducible output; two identical constructions give identical results
  • use None, for non-deterministic behavior
21

Methods:

Name Description
fit

Fit the Feature effect to the data.

eval

Evaluate the mean effect of the feature-th feature at positions xs.

plot

Plot the feature effect.

Source code in effector/global_effect_pdp.py
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
def __init__(
    self,
    data: np.ndarray,
    model: Callable,
    model_jac: Optional[Callable] = None,
    *,
    axis_limits: Optional[np.ndarray] = None,
    nof_instances: Union[int, str] = 10_000,
    schema: Optional[Union[ingestion.Schema, dict]] = None,
    random_state: Optional[int] = 21,
):
    r"""
    Constructor of the DerivativePDP class.

    Definition:
        d-PDP:
        $$
        dPDP(x_s) = {1 \over N} \sum_{i=1}^N {\partial f \over \partial x_s}(x_s, \mathbf{x}_c^i)
        $$

        centered-PDP:
        $$
        dPDP_c(x_s) = dPDP(x_s) - c, \quad c = {1 \over M} \sum_{j=1}^M dPDP(x_s^j)
        $$

        ICE:
        $$
        dICE^i(x_s) = {\partial f \over \partial x_s}(x_s, \mathbf{x}_c^i), \quad i=1, \dots, N
        $$

        centered-ICE:
        $$
        dICE_c^i(x_s) = dICE^i(x_s) - c_i, \quad c_i = {1 \over M} \sum_{j=1}^M dICE^i(x_s^j)
        $$

        heterogeneity function:
        $$
        h(x_s) = {1 \over N} \sum_{i=1}^N ( dICE_c^i(x_s) - dPDP_c(x_s) )^2
        $$

        The heterogeneity value is:
        $$
        \mathcal{H}(x_s) = {1 \over M} \sum_{j=1}^M h(x_s^j),
        $$
        where $x_s^j$ are an equally spaced grid of points in $[x_s^{\min}, x_s^{\max}]$.

    Notes:
        - The required parameters are `data` and `model`. The rest are optional.
        - The `model_jac` is the Jacobian of the model. If `None`, the Jacobian will be computed numerically.

    Args:
        data: the design matrix

            - shape: `(N,D)`
        model: the black-box model. Must be a `Callable` with:

            - input: `ndarray` of shape `(N, D)`
            - output: `ndarray` of shape `(N, )`

        model_jac: the black-box model Jacobian. Must be a `Callable` with:

            - input: `ndarray` of shape `(N, D)`
            - output: `ndarray` of shape `(N, D)`

        axis_limits: The limits of the feature effect plot along each axis

            - use a `ndarray` of shape `(2, D)`, to specify them manually
            - use `None`, to be inferred from the data

        nof_instances: maximum number of instances to be used for PDP.

            - use "all", for using all instances.
            - use an `int`, for using `nof_instances` instances.

        schema: input metadata (R10) — an `effector.Schema` or a plain `dict`
            with any of the keys `feature_names`, `feature_types`,
            `cat_limit`, `target_name`, `scale_x_list`, `scale_y`

            - omitted fields are auto-inferred from `data` (numpy
              heuristics) or synthesized (`["x_0", ...]`, `"y"`); to start
              from a DataFrame use `effector.from_dataframe`
            - explicit fields always win over inference

        random_state: seed for every internal random step (e.g. `nof_instances` subsampling)

            - use an `int` (default: `21`), for reproducible output; two identical constructions give identical results
            - use `None`, for non-deterministic behavior
    """

    super(DerPDP, self).__init__(
        data,
        model,
        model_jac,
        axis_limits=axis_limits,
        nof_instances=nof_instances,
        schema=schema,
        random_state=random_state,
        method_name="d-PDP",
    )

fit(features='all', *, centering=False, points_for_centering=helpers.NOF_INTERNAL_POINTS, use_vectorized=True)

Fit the Feature effect to the data.

Notes

You can use .eval or .plot without calling .fit explicitly. The only thing that .fit does is to compute the normalization constant for centering the PDP and ICE plots. This will be automatically done when calling eval or plot, so there is no need to call fit explicitly.

Parameters:

Name Type Description Default
features Union[int, str, list]

the features to fit. - If set to "all", all the features will be fitted.

'all'
centering Union[bool, str]

whether to center the plot:

  • False means no centering
  • True or zero_integral centers around the y axis.
  • zero_start starts the plot from y=0.
False
points_for_centering int

number of linspaced points along the feature axis used for centering.

NOF_INTERNAL_POINTS
use_vectorized bool

whether to use vectorized operations for the PDP and ICE curves

True
Source code in effector/global_effect_pdp.py
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
def fit(
    self,
    features: Union[int, str, list] = "all",
    *,
    centering: Union[bool, str] = False,
    points_for_centering: int = helpers.NOF_INTERNAL_POINTS,
    use_vectorized: bool = True,
):
    """
    Fit the Feature effect to the data.

    Notes:
        You can use `.eval` or `.plot` without calling `.fit` explicitly.
        The only thing that `.fit` does is to compute the normalization constant for centering the PDP and ICE plots.
        This will be automatically done when calling `eval` or `plot`, so there is no need to call `fit` explicitly.

    Args:
        features: the features to fit.
            - If set to "all", all the features will be fitted.

        centering: whether to center the plot:

            - `False` means no centering
            - `True` or `zero_integral` centers around the `y` axis.
            - `zero_start` starts the plot from `y=0`.

        points_for_centering: number of linspaced points along the feature axis used for centering.
        use_vectorized: whether to use vectorized operations for the PDP and ICE curves

    """
    self._fit_loop(
        features, centering, points_for_centering, use_vectorized=use_vectorized
    )

eval(feature, xs, centering=None, mask=None)

Evaluate the mean effect of the feature-th feature at positions xs.

Notes

This is the one evaluation method of every effect class (R1): it always returns the mean effect as a single (T,) array. Heterogeneity lives on its own surface — eval_heter(feature, xs) for the curve, heter_score(feature) for the scalar, and payload(feature) for the method's raw object.

Parameters:

Name Type Description Default
feature int

index of feature of interest

required
xs ndarray

the points along the s-th axis to evaluate the effect at

  • np.ndarray of shape (T, )
required
centering Union[None, bool, str]

whether to center the effect

  • None (default) uses the class default (DEFAULT_CENTERING)
  • False: no centering
  • True or "zero_integral": center around the y axis
  • "zero_start": the effect starts from y=0
None
mask Optional[ndarray]

optional boolean (N,) selecting a subregion. None (default) evaluates the fitted state; a mask summarizes that subset of the cached local effects on the fly — the effect within the subregion, on the global frame, without model calls. Centering is then computed over the subregion's own interval. Nothing is stored.

None

Returns:

Type Description
ndarray

the mean effect y at the given xs, (T,)

Source code in effector/global_effect.py
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
def eval(
    self,
    feature: int,
    xs: np.ndarray,
    centering: Union[None, bool, str] = None,
    mask: Optional[np.ndarray] = None,
) -> np.ndarray:
    """Evaluate the mean effect of the `feature`-th feature at positions `xs`.

    Notes:
        This is the one evaluation method of every effect class (R1): it
        always returns the mean effect as a single `(T,)` array.
        Heterogeneity lives on its own surface — `eval_heter(feature, xs)`
        for the curve, `heter_score(feature)` for the scalar, and
        `payload(feature)` for the method's raw object.

    Args:
        feature: index of feature of interest
        xs: the points along the s-th axis to evaluate the effect at

          - `np.ndarray` of shape `(T, )`

        centering: whether to center the effect

            - `None` (default) uses the class default (`DEFAULT_CENTERING`)
            - `False`: no centering
            - `True` or `"zero_integral"`: center around the `y` axis
            - `"zero_start"`: the effect starts from `y=0`

        mask: optional boolean `(N,)` selecting a subregion. `None`
            (default) evaluates the fitted state; a mask summarizes that
            subset of the cached local effects on the fly — the effect
            *within* the subregion, on the global frame, without model
            calls. Centering is then computed over the subregion's own
            interval. Nothing is stored.

    Returns:
        the mean effect `y` at the given `xs`, `(T,)`
    """
    centering = self.DEFAULT_CENTERING if centering is None else centering
    centering = helpers.prep_centering(centering)
    mask = self._prep_mask(mask)

    if mask is not None:
        if not self._is_cat(feature):
            self._effective_limits(feature, mask)  # degeneracy guard
        self._ensure_local_effects(feature)
        params = self._summarize(feature, mask, **self._replay_fit_kwargs(feature))
        y = self._eval_masked_mean(feature, xs, params, mask)
        if centering is not False:
            norm_const = self._compute_norm_const(
                feature, method=centering, params=params, mask=mask
            )
            y = y - self._mean_norm_const(norm_const)
        return y

    if self.requires_refit(feature, centering):
        self._refit(feature, centering)

    if not self.axis_limits[0, feature] < self.axis_limits[1, feature]:
        raise ValueError(
            f"Feature {feature} has a degenerate axis interval "
            f"[{self.axis_limits[0, feature]}, {self.axis_limits[1, feature]}]"
        )

    y = self._eval_unnorm(feature, xs)
    if centering is not False:
        norm_const = self.feature_effect["feature_" + str(feature)]["norm_const"]
        y = y - self._mean_norm_const(norm_const)
    return y

plot(feature, heterogeneity='ice', centering=False, nof_points=100, scale_x=None, scale_y=None, nof_ice=100, show_avg_output=False, y_limits=None, use_vectorized=True, show_plot=True, mask=None, feature_label=None)

Plot the feature effect.

Parameters:

Name Type Description Default
feature int

the feature to plot

required
heterogeneity Union[bool, str]

whether to plot the heterogeneity

  • False, plot only the mean effect
  • True or std, plot the standard deviation of the ICE curves
  • ice, also plot the ICE curves
'ice'
centering Union[bool, str]

whether to center the plot

  • False means no centering
  • True or zero_integral centers around the y axis.
  • zero_start starts the plot from y=0.
False
nof_points int

the grid size for the PDP plot

100
scale_x Optional[dict]

None or Dict with keys ['std', 'mean']

  • If set to None, no scaling will be applied.
  • If set to a dict, the x-axis will be scaled x = (x + mean) * std
None
scale_y Optional[dict]

None or Dict with keys ['std', 'mean']

  • If set to None, no scaling will be applied.
  • If set to a dict, the y-axis will be scaled y = (y + mean) * std
None
nof_ice Union[int, str]

number of ICE plots to show on top of the SHAP curve

100
show_avg_output bool

whether to show the average output of the model

False
y_limits Optional[List]

None or tuple, the limits of the y-axis (derivative units)

  • If set to None, the limits of the y-axis are set automatically
  • If set to a tuple, the limits are manually set
None
use_vectorized bool

whether to use the vectorized version of the PDP computation

True
show_plot bool

whether to show the plot

True
mask Optional[ndarray]

optional boolean (N,) selecting a subregion — plot the d-PDP/d-ICE within it from the cached d-ICE table (grid resolution; nof_points does not apply), model-free, with the x-axis windowed to the subregion's own interval

None
feature_label Optional[str]

optional display name for the feature axis (e.g. a regional node's name), overriding feature_names[feature]

None
Source code in effector/global_effect_pdp.py
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
def plot(
    self,
    feature: int,
    heterogeneity: Union[bool, str] = "ice",
    centering: Union[bool, str] = False,
    nof_points: int = 100,
    scale_x: Optional[dict] = None,
    scale_y: Optional[dict] = None,
    nof_ice: Union[int, str] = 100,
    show_avg_output: bool = False,
    y_limits: Optional[List] = None,
    use_vectorized: bool = True,
    show_plot: bool = True,
    mask: Optional[np.ndarray] = None,
    feature_label: Optional[str] = None,
):
    """
    Plot the feature effect.

    Parameters:
        feature: the feature to plot
        heterogeneity: whether to plot the heterogeneity

              - `False`, plot only the mean effect
              - `True` or `std`, plot the standard deviation of the ICE curves
              - `ice`, also plot the ICE curves

        centering: whether to center the plot

            - `False` means no centering
            - `True` or `zero_integral` centers around the `y` axis.
            - `zero_start` starts the plot from `y=0`.

        nof_points: the grid size for the PDP plot

        scale_x: None or Dict with keys ['std', 'mean']

            - If set to None, no scaling will be applied.
            - If set to a dict, the x-axis will be scaled `x = (x + mean) * std`

        scale_y: None or Dict with keys ['std', 'mean']

            - If set to None, no scaling will be applied.
            - If set to a dict, the y-axis will be scaled `y = (y + mean) * std`

        nof_ice: number of ICE plots to show on top of the SHAP curve
        show_avg_output: whether to show the average output of the model

        y_limits: None or tuple, the limits of the y-axis (derivative units)

            - If set to None, the limits of the y-axis are set automatically
            - If set to a tuple, the limits are manually set

        use_vectorized: whether to use the vectorized version of the PDP computation
        show_plot: whether to show the plot
        mask: optional boolean `(N,)` selecting a subregion — plot the
            d-PDP/d-ICE *within* it from the cached d-ICE table (grid
            resolution; `nof_points` does not apply), model-free, with the
            x-axis windowed to the subregion's own interval
        feature_label: optional display name for the feature axis (e.g. a
            regional node's name), overriding `feature_names[feature]`
    """
    ret = self._plot(
        feature,
        heterogeneity,
        centering,
        nof_points,
        scale_x,
        scale_y,
        nof_ice,
        show_avg_output,
        y_limits,
        use_vectorized,
        show_plot,
        mask,
        feature_label,
    )

    if not show_plot:
        fig, ax = ret
        return fig, ax

effector.global_effect_shap.ShapDP(data, model, *, axis_limits=None, nof_instances=1000, schema=None, random_state=21, shap_values=None, backend='shap', budget=512, shap_explainer_kwargs=None, shap_explanation_kwargs=None)

Bases: GlobalEffectBase

Constructor of the ShapDP class.

Definition

The value of a coalition of \(S\) features is estimated as: $$ \hat{v}(S) = {1 \over N} \sum_{i=1}^N [f(\mathbf{x}_S \cup \mathbf{x}_C^i) - f(\mathbf{x}^i) ] $$ \(\hat{v}(S)\) quantifies the contribution when the features in \(S\) are set to \(\mathbf{x}_S\). For all instances, we compute two outputs:

  • \(f(\mathbf{x}_S \cup \mathbf{x}_C^i)\) is the output of the model when the features in \(S\) are set to \(\mathbf{x}_S\) and the rest of the features are left as they are
  • \(f(\mathbf{x}^i)\) is the output of the model when the instance is left as is The average difference (over all instances) between these two outputs is the value of the coalition \(S\).

The contribution of a feature \(j\) added to a coalition \(S\) is estimated as: $$ \hat{\Delta}_{S, j} = \hat{v}(S \cup {j}) - \hat{v}(S) $$

The SHAP value of a feature \(j\) with value \(x_j\) is the average contribution of feature \(j\) across all possible coalitions with a weight \(w_{S, j}\):

\[ \hat{\phi}_j(x_j) = {1 \over N} \sum_{S \subseteq \{1, \dots, D\} \setminus \{j\}} w_{S, j} \hat{\Delta}_{S, j} \]

where \(w_{S, j}\) assures that the contribution of feature \(j\) is the same for all coalitions of the same size. For example, there are \(D-1\) ways for \(x_j\) to enter a coalition of \(|S| = 1\) feature, so \(w_{S, j} = {1 \over D (D-1)}\) for each of them. In contrast, there is only one way for \(x_j\) to enter a coaltion of \(|S|=0\) (to be the first specified feature), so \(w_{S, j} = {1 \over D}\).

The SHAP Dependence Plot (SHAP-DP) is a spline \(\hat{f}^{SDP}_j(x_j)\) fit to the dataset \(\{(x_j^i, \hat{\phi}_j(x_j^i))\}_{i=1}^N\) using the UnivariateSpline function from scipy.interpolate.

Notes
  • The required parameters are data and model. The rest are optional.
  • SHAP values are computed using either the shap package (backend="shap") or the shapiq package (backend="shapiq").
  • SHAP values are centered by default, i.e., the average SHAP value is subtracted from the SHAP values.
  • More details on the SHAP values can be found in the original paper and in the book Interpreting Machine Learning Models with SHAP

Parameters:

Name Type Description Default
data ndarray

the design matrix

  • shape: (N,D)
required
model Callable

the black-box model. Must be a Callable with:

  • input: ndarray of shape (N, D)
  • output: ndarray of shape (N,)
required
axis_limits Optional[ndarray]

The limits of the feature effect plot along each axis

  • use a ndarray of shape (2, D), to specify them manually
  • use None, to be inferred from the data
None
nof_instances Union[int, str]

maximum number of instances to be used for SHAP estimation.

  • use "all", for using all instances.
  • use an int, for using nof_instances instances.
1000
schema Optional[Union[Schema, dict]]

input metadata (R10) — an effector.Schema or a plain dict with any of the keys feature_names, feature_types, cat_limit, target_name, scale_x_list, scale_y

  • omitted fields are auto-inferred from data (numpy heuristics) or synthesized (["x_0", ...], "y"); to start from a DataFrame use effector.from_dataframe
  • explicit fields always win over inference
None
random_state Optional[int]

seed for every internal random step (nof_instances subsampling and the shap/shapiq explainer, unless overridden via shap_explainer_kwargs)

  • use an int (default: 21), for reproducible output; two identical constructions give identical results
  • use None, for non-deterministic behavior
21
shap_values Optional[ndarray]

The SHAP values of the model

  • if shap values are already computed, they can be passed here
  • if None, the SHAP values will be computed using the shap package
None
backend str

Package to compute SHAP values

  • use "shap" for the shap package (default)
  • use "shapiq" for the shapiq package
'shap'
budget int

budget for the SHAP approximation (default 512)

  • increasing the budget improves the approximation at the cost of slower computation
512
shap_explainer_kwargs Optional[dict]

keyword arguments for the shap.Explainer / shapiq.Explainer (depending on backend). The constructor's random_state is used as the backend seed (seed= for shap, random_state= for shapiq) unless you pass your own here. See effector.global_effect_shap._compute_shap_values — the single place the explainer is constructed and invoked.

None
shap_explanation_kwargs Optional[dict]

keyword arguments for computing the SHAP values with the chosen backend (same code path as above).

None
Notes

SHAP values are expensive to compute. To speed up the computation consider using a subset of the dataset. The nof_instances parameter controls the number of instances used for computing the SHAP values. The default value is 1_000 instances, which is a good trade-off between speed and accuracy.

Methods:

Name Description
fit

Fit the SHAP Dependence Plot to the data.

eval

Evaluate the mean effect of the feature-th feature at positions xs.

plot

Plot the SHAP Dependence Plot (SDP) of the s-th feature.

Source code in effector/global_effect_shap.py
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
def __init__(
    self,
    data: np.ndarray,
    model: Callable,
    *,
    axis_limits: Optional[np.ndarray] = None,
    nof_instances: Union[int, str] = 1_000,
    schema: Optional[Union[ingestion.Schema, dict]] = None,
    random_state: Optional[int] = 21,
    shap_values: Optional[np.ndarray] = None,
    backend: str = "shap",
    budget: int = 512,
    shap_explainer_kwargs: Optional[dict] = None,
    shap_explanation_kwargs: Optional[dict] = None,
):
    r"""
    Constructor of the ShapDP class.

    ??? note "Definition"

        The value of a coalition of $S$ features is estimated as:
        $$
        \hat{v}(S) = {1 \over N} \sum_{i=1}^N [f(\mathbf{x}_S \cup \mathbf{x}_C^i) - f(\mathbf{x}^i) ]
        $$
        $\hat{v}(S)$ quantifies the contribution when the features in $S$ are set to $\mathbf{x}_S$.
        For all instances, we compute two outputs:

          - $f(\mathbf{x}_S \cup \mathbf{x}_C^i)$ is the output of the model when the features in $S$ are set to $\mathbf{x}_S$ and the rest of the features are left as they are
          - $f(\mathbf{x}^i)$ is the output of the model when the instance is left as is
        The average difference (over all instances) between these two outputs is the value of the coalition $S$.

        The contribution of a feature $j$ added to a coalition $S$ is estimated as:
        $$
        \hat{\Delta}_{S, j} = \hat{v}(S \cup \{j\}) - \hat{v}(S)
        $$

        The SHAP value of a feature $j$ with value $x_j$ is the average contribution of feature $j$ across all possible coalitions with a weight $w_{S, j}$:

        $$
        \hat{\phi}_j(x_j) = {1 \over N} \sum_{S \subseteq \{1, \dots, D\} \setminus \{j\}} w_{S, j} \hat{\Delta}_{S, j}
        $$

        where $w_{S, j}$ assures that the contribution of feature $j$ is the same for all coalitions of the same size. For example, there are $D-1$ ways for $x_j$ to enter a coalition of $|S| = 1$ feature, so $w_{S, j} = {1 \over D (D-1)}$ for each of them. In contrast, there is only one way for $x_j$ to enter a coaltion of $|S|=0$ (to be the first specified feature), so $w_{S, j} = {1 \over D}$.

        The SHAP Dependence Plot (SHAP-DP) is a spline $\hat{f}^{SDP}_j(x_j)$ fit to the dataset $\{(x_j^i, \hat{\phi}_j(x_j^i))\}_{i=1}^N$ using the `UnivariateSpline` function from `scipy.interpolate`.

    ??? note "Notes"

        * The required parameters are `data` and `model`. The rest are optional.
        * SHAP values are computed using either the [`shap`](https://shap.readthedocs.io/en/latest/) package (`backend="shap"`) or the [`shapiq`](https://shapiq.readthedocs.io/en/latest/) package (`backend="shapiq"`).
        * SHAP values are centered by default, i.e., the average SHAP value is subtracted from the SHAP values.
        * More details on the SHAP values can be found in the [original paper](https://arxiv.org/abs/1705.07874) and in the book [Interpreting Machine Learning Models with SHAP](https://christophmolnar.com/books/shap/)

    Args:
        data: the design matrix

            - shape: `(N,D)`
        model: the black-box model. Must be a `Callable` with:

            - input: `ndarray` of shape `(N, D)`
            - output: `ndarray` of shape `(N,)`

        axis_limits: The limits of the feature effect plot along each axis

            - use a `ndarray` of shape `(2, D)`, to specify them manually
            - use `None`, to be inferred from the data

        nof_instances: maximum number of instances to be used for SHAP estimation.

            - use `"all"`, for using all instances.
            - use an `int`, for using `nof_instances` instances.

        schema: input metadata (R10) — an `effector.Schema` or a plain `dict`
            with any of the keys `feature_names`, `feature_types`,
            `cat_limit`, `target_name`, `scale_x_list`, `scale_y`

            - omitted fields are auto-inferred from `data` (numpy
              heuristics) or synthesized (`["x_0", ...]`, `"y"`); to start
              from a DataFrame use `effector.from_dataframe`
            - explicit fields always win over inference

        random_state: seed for every internal random step (`nof_instances` subsampling and the shap/shapiq explainer, unless overridden via `shap_explainer_kwargs`)

            - use an `int` (default: `21`), for reproducible output; two identical constructions give identical results
            - use `None`, for non-deterministic behavior

        shap_values: The SHAP values of the model

            - if shap values are already computed, they can be passed here
            - if `None`, the SHAP values will be computed using the `shap` package

        backend: Package to compute SHAP values

            - use `"shap"` for the `shap` package (default)
            - use `"shapiq"` for the `shapiq` package

        budget: budget for the SHAP approximation (default 512)

            - increasing the budget improves the approximation at the cost of slower computation

        shap_explainer_kwargs: keyword arguments for the `shap.Explainer` /
            `shapiq.Explainer` (depending on `backend`). The constructor's
            `random_state` is used as the backend seed (`seed=` for `shap`,
            `random_state=` for `shapiq`) unless you pass your own here.
            See `effector.global_effect_shap._compute_shap_values` — the
            single place the explainer is constructed and invoked.
        shap_explanation_kwargs: keyword arguments for computing the SHAP
            values with the chosen backend (same code path as above).

    Notes:
        SHAP values are expensive to compute.
        To speed up the computation consider using a subset of the dataset.
        The `nof_instances` parameter controls the number of instances used for computing the SHAP values.
        The default value is `1_000` instances, which is a good trade-off between speed and accuracy.
    """
    self.shap_values = shap_values if shap_values is not None else None
    if backend not in ["shap", "shapiq"]:
        raise ValueError(f"Invalid backend: {backend!r}; use 'shap' or 'shapiq'")
    self.backend = backend
    self.budget = budget
    self.shap_explainer_kwargs = shap_explainer_kwargs
    self.shap_explanation_kwargs = shap_explanation_kwargs
    super(ShapDP, self).__init__(
        "SHAP DP",
        data,
        model,
        nof_instances=nof_instances,
        axis_limits=axis_limits,
        schema=schema,
        random_state=random_state,
    )

fit(features='all', *, centering=True, points_for_centering=helpers.NOF_INTERNAL_POINTS, binning_method='dp', binning_scope='global')

Fit the SHAP Dependence Plot to the data.

Notes

The SHAP Dependence Plot (SDP) \(\hat{f}^{SDP}_j(x_j)\) is a spline fit to the dataset \(\{(x_j^i, \hat{\phi}_j(x_j^i))\}_{i=1}^N\) using the UnivariateSpline function from scipy.interpolate.

The SHAP standard deviation, \(\hat{\sigma}^{SDP}_j(x_j)\), is a spline fit to the absolute value of the residuals, i.e., to the dataset \(\{(x_j^i, |\hat{\phi}_j(x_j^i) - \hat{f}^{SDP}_j(x_j^i)|)\}_{i=1}^N\), using the UnivariateSpline function from scipy.interpolate.

Parameters:

Name Type Description Default
features Union[int, str, List]

the features to fit. - If set to "all", all the features will be fitted.

'all'
centering Union[bool, str]
  • If set to False, no centering will be applied.
  • If set to "zero_integral" or True, the integral of the feature effect will be set to zero.
  • If set to "zero_mean", the mean of the feature effect will be set to zero.
True
points_for_centering int

number of linspaced points along the feature axis used for centering.

NOF_INTERNAL_POINTS
binning_method Union[str, DynamicProgramming, Agglomerative, Quantile, Fixed]

the binning method to be used for fitting a piecewise linear function to the SHAP values.

  • If set to "greedy", the greedy binning method will be used.
  • If set to "fixed", the fixed binning method will be used.
'dp'
binning_scope str

the x-range the binner covers when a masked summary re-bins a subregion (eval/eval_heter/plot/ heter_score with mask=; the regional split search)

  • "global" (default): the frozen global axis_limits — one frame for every subregion, directly comparable
  • "effective": the masked column's own [min, max] — bins packed into the subregion, finer resolution

Recorded at fit and replayed by every masked call, so the split search and the display always share the same scope. Ignored when no mask is involved.

'global'
Source code in effector/global_effect_shap.py
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
def fit(
    self,
    features: Union[int, str, List] = "all",
    *,
    centering: Union[bool, str] = True,
    points_for_centering: int = helpers.NOF_INTERNAL_POINTS,
    binning_method: Union[
        str, ap.DynamicProgramming, ap.Agglomerative, ap.Quantile, ap.Fixed
    ] = "dp",
    binning_scope: str = "global",
) -> None:
    r"""Fit the SHAP Dependence Plot to the data.

    Notes:
        The SHAP Dependence Plot (SDP) $\hat{f}^{SDP}_j(x_j)$ is a spline fit to
        the dataset $\{(x_j^i, \hat{\phi}_j(x_j^i))\}_{i=1}^N$
        using the `UnivariateSpline` function from `scipy.interpolate`.

        The SHAP standard deviation, $\hat{\sigma}^{SDP}_j(x_j)$, is a spline fit            to the absolute value of the residuals, i.e., to the dataset $\{(x_j^i, |\hat{\phi}_j(x_j^i) - \hat{f}^{SDP}_j(x_j^i)|)\}_{i=1}^N$, using the `UnivariateSpline` function from `scipy.interpolate`.

    Args:
        features: the features to fit.
            - If set to "all", all the features will be fitted.
        centering:
            - If set to False, no centering will be applied.
            - If set to "zero_integral" or True, the integral of the feature effect will be set to zero.
            - If set to "zero_mean", the mean of the feature effect will be set to zero.

        points_for_centering: number of linspaced points along the feature axis used for centering.

        binning_method: the binning method to be used for fitting a piecewise linear function to the SHAP values.

            - If set to "greedy", the greedy binning method will be used.
            - If set to "fixed", the fixed binning method will be used.

        binning_scope: the x-range the binner covers when a *masked*
            summary re-bins a subregion (`eval`/`eval_heter`/`plot`/
            `heter_score` with `mask=`; the regional split search)

            - `"global"` (default): the frozen global `axis_limits` — one
              frame for every subregion, directly comparable
            - `"effective"`: the masked column's own `[min, max]` — bins
              packed into the subregion, finer resolution

            Recorded at fit and replayed by every masked call, so the
            split search and the display always share the same scope.
            Ignored when no mask is involved.
    """
    check_binning_scope(binning_scope)
    self._fit_loop(
        features,
        centering,
        points_for_centering,
        binning_method=binning_method,
        binning_scope=binning_scope,
    )

eval(feature, xs, centering=None, mask=None)

Evaluate the mean effect of the feature-th feature at positions xs.

Notes

This is the one evaluation method of every effect class (R1): it always returns the mean effect as a single (T,) array. Heterogeneity lives on its own surface — eval_heter(feature, xs) for the curve, heter_score(feature) for the scalar, and payload(feature) for the method's raw object.

Parameters:

Name Type Description Default
feature int

index of feature of interest

required
xs ndarray

the points along the s-th axis to evaluate the effect at

  • np.ndarray of shape (T, )
required
centering Union[None, bool, str]

whether to center the effect

  • None (default) uses the class default (DEFAULT_CENTERING)
  • False: no centering
  • True or "zero_integral": center around the y axis
  • "zero_start": the effect starts from y=0
None
mask Optional[ndarray]

optional boolean (N,) selecting a subregion. None (default) evaluates the fitted state; a mask summarizes that subset of the cached local effects on the fly — the effect within the subregion, on the global frame, without model calls. Centering is then computed over the subregion's own interval. Nothing is stored.

None

Returns:

Type Description
ndarray

the mean effect y at the given xs, (T,)

Source code in effector/global_effect.py
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
def eval(
    self,
    feature: int,
    xs: np.ndarray,
    centering: Union[None, bool, str] = None,
    mask: Optional[np.ndarray] = None,
) -> np.ndarray:
    """Evaluate the mean effect of the `feature`-th feature at positions `xs`.

    Notes:
        This is the one evaluation method of every effect class (R1): it
        always returns the mean effect as a single `(T,)` array.
        Heterogeneity lives on its own surface — `eval_heter(feature, xs)`
        for the curve, `heter_score(feature)` for the scalar, and
        `payload(feature)` for the method's raw object.

    Args:
        feature: index of feature of interest
        xs: the points along the s-th axis to evaluate the effect at

          - `np.ndarray` of shape `(T, )`

        centering: whether to center the effect

            - `None` (default) uses the class default (`DEFAULT_CENTERING`)
            - `False`: no centering
            - `True` or `"zero_integral"`: center around the `y` axis
            - `"zero_start"`: the effect starts from `y=0`

        mask: optional boolean `(N,)` selecting a subregion. `None`
            (default) evaluates the fitted state; a mask summarizes that
            subset of the cached local effects on the fly — the effect
            *within* the subregion, on the global frame, without model
            calls. Centering is then computed over the subregion's own
            interval. Nothing is stored.

    Returns:
        the mean effect `y` at the given `xs`, `(T,)`
    """
    centering = self.DEFAULT_CENTERING if centering is None else centering
    centering = helpers.prep_centering(centering)
    mask = self._prep_mask(mask)

    if mask is not None:
        if not self._is_cat(feature):
            self._effective_limits(feature, mask)  # degeneracy guard
        self._ensure_local_effects(feature)
        params = self._summarize(feature, mask, **self._replay_fit_kwargs(feature))
        y = self._eval_masked_mean(feature, xs, params, mask)
        if centering is not False:
            norm_const = self._compute_norm_const(
                feature, method=centering, params=params, mask=mask
            )
            y = y - self._mean_norm_const(norm_const)
        return y

    if self.requires_refit(feature, centering):
        self._refit(feature, centering)

    if not self.axis_limits[0, feature] < self.axis_limits[1, feature]:
        raise ValueError(
            f"Feature {feature} has a degenerate axis interval "
            f"[{self.axis_limits[0, feature]}, {self.axis_limits[1, feature]}]"
        )

    y = self._eval_unnorm(feature, xs)
    if centering is not False:
        norm_const = self.feature_effect["feature_" + str(feature)]["norm_const"]
        y = y - self._mean_norm_const(norm_const)
    return y

plot(feature, heterogeneity='shap_values', centering=True, nof_points=100, scale_x=None, scale_y=None, nof_shap_values=100, show_avg_output=False, y_limits=None, only_shap_values=False, show_plot=True, mask=None, feature_label=None)

Plot the SHAP Dependence Plot (SDP) of the s-th feature.

Parameters:

Name Type Description Default
feature int

index of the plotted feature

required
heterogeneity Union[bool, str]

whether to output the heterogeneity of the SHAP values

  • If heterogeneity is False, no heterogeneity is plotted
  • If heterogeneity is True or "std", the standard deviation of the shap values is plotted
  • If heterogeneity is "shap_values", the shap values are scattered on top of the SHAP curve
'shap_values'
centering Union[bool, str]

whether to center the SDP

  • If centering is False, the SHAP curve is not centered
  • If centering is True or zero_integral, the SHAP curve is centered around the y axis.
  • If centering is zero_start, the SHAP curve starts from y=0.
True
nof_points int

number of points to evaluate the SDP plot

100
scale_x Optional[dict]

dictionary with keys "mean" and "std" for scaling the x-axis

None
scale_y Optional[dict]

dictionary with keys "mean" and "std" for scaling the y-axis

None
nof_shap_values Union[int, str]

number of shap values to show on top of the SHAP curve

100
show_avg_output bool

whether to show the average output of the model

False
y_limits Optional[List]

limits of the y-axis

None
only_shap_values bool

whether to plot only the shap values

False
show_plot bool

whether to show the plot

True
mask Optional[ndarray]

optional boolean (N,) selecting a subregion — plot the SHAP-DP within it (the masked φ re-binned/re-splined from the cached attributions, no model calls), with the x-axis windowed to the subregion's own interval

None
feature_label Optional[str]

optional display name for the feature axis (e.g. a regional node's name), overriding feature_names[feature]

None
Source code in effector/global_effect_shap.py
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
def plot(
    self,
    feature: int,
    heterogeneity: Union[bool, str] = "shap_values",
    centering: Union[bool, str] = True,
    nof_points: int = 100,
    scale_x: Optional[dict] = None,
    scale_y: Optional[dict] = None,
    nof_shap_values: Union[int, str] = 100,
    show_avg_output: bool = False,
    y_limits: Optional[List] = None,
    only_shap_values: bool = False,
    show_plot: bool = True,
    mask: Optional[np.ndarray] = None,
    feature_label: Optional[str] = None,
) -> Union[Tuple, None]:
    """
    Plot the SHAP Dependence Plot (SDP) of the s-th feature.

    Args:
        feature: index of the plotted feature
        heterogeneity: whether to output the heterogeneity of the SHAP values

            - If `heterogeneity` is `False`, no heterogeneity is plotted
            - If `heterogeneity` is `True` or `"std"`, the standard deviation of the shap values is plotted
            - If `heterogeneity` is `"shap_values"`, the shap values are scattered on top of the SHAP curve

        centering: whether to center the SDP

            - If `centering` is `False`, the SHAP curve is not centered
            - If `centering` is `True` or `zero_integral`, the SHAP curve is centered around the `y` axis.
            - If `centering` is `zero_start`, the SHAP curve starts from `y=0`.

        nof_points: number of points to evaluate the SDP plot
        scale_x: dictionary with keys "mean" and "std" for scaling the x-axis
        scale_y: dictionary with keys "mean" and "std" for scaling the y-axis
        nof_shap_values: number of shap values to show on top of the SHAP curve
        show_avg_output: whether to show the average output of the model
        y_limits: limits of the y-axis
        only_shap_values: whether to plot only the shap values
        show_plot: whether to show the plot
        mask: optional boolean `(N,)` selecting a subregion — plot the
            SHAP-DP *within* it (the masked φ re-binned/re-splined from the
            cached attributions, no model calls), with the x-axis windowed
            to the subregion's own interval
        feature_label: optional display name for the feature axis (e.g. a
            regional node's name), overriding `feature_names[feature]`
    """
    heterogeneity = helpers.prep_confidence_interval(heterogeneity)
    centering = helpers.prep_centering(centering)
    scale_x = helpers.resolve_scale(
        scale_x, self.scale_x_list[feature] if self.scale_x_list else None
    )
    scale_y = helpers.resolve_scale(scale_y, self.scale_y)
    mask = self._prep_mask(mask)
    feature_names = list(self.feature_names)
    if feature_label is not None:
        feature_names[feature] = feature_label

    if mask is not None:
        # transient subregion payload from the cached attributions —
        # pure numpy, nothing stored (the masked-plot path)
        if not self._is_cat(feature):
            self._effective_limits(feature, mask)  # degeneracy guard
        self._ensure_local_effects(feature)
        m_params = self._summarize(
            feature, mask, **self._replay_fit_kwargs(feature)
        )
        m_norm = (
            self._compute_norm_const(
                feature, method=centering, params=m_params, mask=mask
            )
            if centering is not False
            else 0.0
        )

    params_key = "feature_" + str(feature)
    if self._is_cat(feature):
        if mask is not None:
            # the masked payload's frame: the levels observed *within* the
            # subregion (its φ were re-binned per level by _summarize)
            params = m_params
            levels, labels = self._level_display(feature, params["levels"])
            y_levels = self._eval_unnorm(feature, levels, params=params) - m_norm
            data = self.data[mask]
        else:
            # fit if needed, then draw per-level bars (+ the shap cloud)
            self.eval(feature, self._levels(feature)[:1], centering=centering)
            params = self.feature_effect[params_key]
            levels, labels = self._level_display(feature)
            y_levels = self.eval(feature, levels, centering=centering)
            data = self.data
        avg_output = (
            helpers.prep_avg_output(data, self.model, None, scale_y)
            if show_avg_output
            else None
        )
        title = "SHAP Dependence Plot (SHAP-DP)"
        if heterogeneity == "shap_values":
            yy = params["yy"]
            if centering is not False:
                yy = yy - (m_norm if mask is not None else params["norm_const"])
            return vis.plot_shap_categorical(
                levels,
                y_levels,
                params["xx"],
                yy,
                feature,
                title=title,
                level_labels=labels,
                scale_x=scale_x,
                scale_y=scale_y,
                avg_output=avg_output,
                feature_names=feature_names,
                target_name=self.target_name,
                nof_shap_values=nof_shap_values,
                y_limits=y_limits,
                show_plot=show_plot,
                random_state=self.random_state,
            )
        variances = (
            self._eval_unnorm(feature, levels, heterogeneity=True, params=params)[1]
            if heterogeneity is not False
            else None
        )
        return vis.plot_categorical_effect(
            levels,
            y_levels,
            variances,
            feature,
            heterogeneity,
            title=title,
            level_labels=labels,
            scale_x=scale_x,
            scale_y=scale_y,
            avg_output=avg_output,
            feature_names=feature_names,
            target_name=self.target_name,
            y_limits=y_limits,
            show_plot=show_plot,
        )

    if mask is not None:
        # the masked payload's frame: x spans the subregion's effective
        # interval, the cloud is the masked φ (already filtered by
        # _summarize) — model-free
        lo, hi = self._effective_limits(feature, mask)
        x = np.linspace(lo, hi, nof_points)
        y = self._eval_unnorm(feature, x, params=m_params)
        if centering is not False:
            y = y - m_norm
        y_std = (
            np.sqrt(np.maximum(m_params["spline_var"](x), 0.0))
            if heterogeneity == "std"
            else None
        )
        _, ind = helpers.prep_nof_instances(
            nof_shap_values, len(m_params["yy"]), self.random_state
        )
        yy = m_params["yy"][ind] if heterogeneity == "shap_values" else None
        if yy is not None and centering is not False:
            yy = yy - m_norm
        xx = m_params["xx"][ind] if heterogeneity == "shap_values" else None
        data = self.data[mask]
    else:
        x = np.linspace(
            self.axis_limits[0, feature], self.axis_limits[1, feature], nof_points
        )

        # get the SHAP curve
        y = self.eval(feature, x, centering=centering)
        y_std = (
            np.sqrt(self.feature_effect["feature_" + str(feature)]["spline_var"](x))
            if heterogeneity == "std"
            else None
        )

        # get some SHAP values
        _, ind = helpers.prep_nof_instances(
            nof_shap_values, self.data.shape[0], self.random_state
        )
        yy = (
            self.feature_effect["feature_" + str(feature)]["yy"][ind]
            if heterogeneity == "shap_values"
            else None
        )
        if yy is not None and centering is not False:
            yy = yy - self.feature_effect["feature_" + str(feature)]["norm_const"]
        xx = (
            self.feature_effect["feature_" + str(feature)]["xx"][ind]
            if heterogeneity == "shap_values"
            else None
        )
        data = self.data

    if show_avg_output:
        avg_output = helpers.prep_avg_output(data, self.model, None, scale_y)
    else:
        avg_output = None

    ret = vis.plot_shap(
        x,
        y,
        xx,
        yy,
        y_std,
        feature,
        heterogeneity=heterogeneity,
        scale_x=scale_x,
        scale_y=scale_y,
        avg_output=avg_output,
        feature_names=feature_names,
        target_name=self.target_name,
        y_limits=y_limits,
        only_shap_values=only_shap_values,
        show_plot=show_plot,
    )

    return ret