givasile/effector

Effector - a Python package for global and regional effect methods

Jupyter Notebook

121

560 commits

updated Oct 6, 2026

See the code

README

Effector

PyPI version Execute Tests codecov Publish Documentation PyPI Downloads Code style: ruff


effector is an eXplainable AI package for tabular data. It:


📖 Documentation | 🚀 Quickstart | 🔧 API | 🏗 Examples


Installation

Effector requires Python 3.10+:

pip install effector

This installs a lightweight core (numpy, scipy, matplotlib, tqdm) that covers PDP, ALE, RHALE and their regional effects.

ShapDP needs the heavier shap/shapiq backends (which pull in numba, scikit-learn, pandas, ...). Install them only if you use that method:

pip install effector[shap]

Quickstart

(a) The inputs

A dataset as a numpy array, a model as a numpy → numpy callable, and, optionally, a schema so the explanation speaks your vocabulary:

import effector
from sklearn.ensemble import HistGradientBoostingRegressor

data = effector.datasets.BikeSharing()  # standardized numpy arrays
model = HistGradientBoostingRegressor(random_state=21).fit(data.x_train, data.y_train)
predict = effector.adapters.from_sklearn(model)  # a plain numpy -> numpy callable

schema = effector.Schema(
    feature_names=data.feature_names,
    feature_types=[
        "nominal", "nominal", "ordinal", "ordinal", "nominal", "nominal",
        "nominal", "ordinal", "continuous", "continuous", "continuous",
    ],
    category_names=[
        ["winter", "spring", "summer", "fall"],
        ["2011", "2012"],
        ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
        None,
        ["no", "yes"],
        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
        ["no", "yes"],
        ["clear", "mist", "light rain/snow", "heavy rain"],
        None, None, None,
    ],
    scale_x_list=[
        {"mean": data.x_train_mu[i], "std": data.x_train_std[i]}
        for i in range(data.x_train.shape[1])
    ],
    scale_y={"mean": data.y_train_mu, "std": data.y_train_std},
    target_name="bike-rentals",
)

📄 In depth: the input layer; pandas, sklearn, torch, classifiers, feature types, units.

(b) The one-liner

report = effector.explain(
    data.x_train, predict, y=data.y_train, schema=schema, nof_instances=5000
)

It fits once, ranks the features, hunts for subregions where the average is hiding something, keeps only the splits that pay for themselves, and opens with the one number worth having:

[effector] global effects   (GAM)  -> 72.3% of the model's variance
           regional effects (CALM) -> 92.0%

The result is a Report, a value you can export or print:

report.to_html("report.html")  # a single self-contained page; mail it, commit it
report.show()                  # the same story, as terminal tables
  ════════════════════════════════════════════════════════════════════════
  PDP report  ·  target: bike-rentals
  ════════════════════════════════════════════════════════════════════════

  DATA & MODEL
  ────────────────────────────────────────────────────────────────────────
    instances     5,000
    features      11  ·  5 nominal · 3 ordinal · 3 continuous
    model output  mean 188 · std 176 · range [-19.5, 948]
    model R²      0.960  (on this subsample)

  EXPLAINED VARIANCE
  ────────────────────────────────────────────────────────────────────────
    step         split on                 solo     ΔR²      R²       heter
    ──────────────────────────────────────────────────────────────────────
    GAM          (all features global)       —       —   72.3%           —
  + hr           temp, workingday, yr   +18.3%  +18.3%   90.6% 0.47 → 0.26
  + temp         hr, hum                 +2.4%   +1.4%   92.0% 0.22 → 0.19
    ──────────────────────────────────────────────────────────────────────
    FINAL                                                92.0%

  REJECTED SPLITS                                            min gain 1.0%
  ────────────────────────────────────────────────────────────────────────
    feature      split on                 solo     ΔR²    reason
    ──────────────────────────────────────────────────────────────────────
  ✗ yr           hr, workingday          +2.7%   -0.8%    redundant
  ✗ hum          hr, temp                +2.2%   +0.2%    below threshold
  ✗ weekday      hr, temp, yr            +0.4%   +0.2%    below threshold
  ✗ workingday   hr, yr                  +6.2%   -4.8%    redundant

    ✗ redundant: it would explain variance on its own (see solo),
      but the accepted splits already account for it.

  FEATURES                                ranked, in the selected snapshot
  ────────────────────────────────────────────────────────────────────────
    feature        importance                          heter      #regions
    ──────────────────────────────────────────────────────────────────────
    hr                 0.7273  ██████████████████     0.2608             4
    yr                 0.2271  ██████                 0.2088             1
    temp               0.2098  █████                  0.1874             4
    hum                0.0932  ██                     0.1221             1
    ──────────────────────────────────────────────────────────────────────
    the features above carry 82% of the total importance mass

(the accepted partition trees follow)

📄 In depth: the report; the explained variance ledger, the triage plane, the regional analysis, every knob of explain(...).

(c) The interactive API

The same engine, as a live handle you query as you go:

pdp = effector.PDP(data.x_train, predict, schema=schema, nof_instances=5000)

pdp.plot("hr")                        # the global effect, ICE curves behind it
pdp.heter_score("hr")                 # 0.47: the average hides a lot
partition = pdp.find_regions("hr")    # where is it hiding it?
partition.show()
Feature 3 - Full partition tree:
🌳 Full Tree Structure:
───────────────────────
hr 🔹 [id: 0 | heter: 0.47 | inst: 5000 | w: 1.00]
    workingday = no 🔹 [id: 1 | heter: 0.33 | inst: 1545 | w: 0.31]
        temp < 6.86 🔹 [id: 2 | heter: 0.22 | inst: 778 | w: 0.16]
        temp ≥ 6.86 🔹 [id: 3 | heter: 0.26 | inst: 767 | w: 0.15]
    workingday = yes 🔹 [id: 4 | heter: 0.34 | inst: 3455 | w: 0.69]
        yr = 2011 🔹 [id: 5 | heter: 0.23 | inst: 1720 | w: 0.34]
        yr = 2012 🔹 [id: 6 | heter: 0.31 | inst: 1735 | w: 0.35]
--------------------------------------------------
Feature 3 - Statistics per tree level:
🌳 Tree Summary:
─────────────────
Level 0🔹heter: 0.47
    Level 1🔹heter: 0.34 | 🔻0.13 (28.12%)
        Level 2🔹heter: 0.26 | 🔻0.08 (22.91%)

The effect of hr depends on workingday (commute peaks vs a midday plateau), and within each branch on temp and yr. Plot the effect inside a region:

partition.plot(2)  # hr, on non-working days, in the cold
Global effect of hr with ICE curvesEffect of hr on cold non-working days

Every verb answers one question:

verbquestion it answers
.plot(f)what does feature f do?
.eval(f, xs)…as numbers, on my grid
.importance(f)how much does f move the output?
.heter_score(f)is the average hiding something?
.find_regions(f)where is it hiding it?
.select_regions()which splits actually earn their keep?
.fit(features, **cfg)(optional) tune the method first

📄 In depth: the interactive API; construct and fit, customizing .fit(), plot, eval, scores, regions.


Documentation map

Start here:

Going deeper:


Supported Methods

Every method computes global effects, and regional effects via .find_regions(feature):

MethodClassReferenceML modelSpeed
PDPPDPFriedman, 2001anyfast for a small dataset
d-PDPDerPDPGoldstein et al., 2013differentiablefast for a small dataset
ALEALEApley & Zhu, 2020anyfast
RHALERHALEGkolemis et al., 2023differentiablevery fast
SHAP-DPShapDPLundberg & Lee, 2017anyfast for a small dataset and a light model

Choosing a method

Three questions decide: is the dataset small (N < 10K) or large? Is the model light (< 0.1s per call) or heavy? Is it differentiable or not?

your caseuse
small + lightany: PDP, ALE, ShapDP; plus RHALE, DerPDP if differentiable
small + heavyPDP, ALE; plus RHALE, DerPDP if differentiable
large + differentiableRHALE
large + non-differentiableALE

Citation

If you use effector, please cite it:

@misc{gkolemis2024effector,
  title={effector: A Python package for regional explanations},
  author={Vasilis Gkolemis et al.},
  year={2024},
  eprint={2404.02629},
  archivePrefix={arXiv},
  primaryClass={cs.LG}
}

Spotlight on effector

🎤 Talks & Presentations

🌍 Adoption & Collaborations

🔍 Additional Resources

Papers that have inspired effector:

  • REPID: Regional Effects in Predictive Models
    Herbinger et al., 2022 - Link

  • Decomposing Global Feature Effects Based on Feature Interactions
    Herbinger et al., 2023 - Link

  • RHALE: Robust Heterogeneity-Aware Effects
    Gkolemis Vasilis et al., 2023 - Link

  • DALE: Decomposing Global Feature Effects
    Gkolemis Vasilis et al., 2023 - Link

  • Greedy Function Approximation: A Gradient Boosting Machine
    Friedman, 2001 - Link

  • Visualizing Predictor Effects in Black-Box Models
    Apley, 2016 - Link

  • SHAP: A Unified Approach to Model Interpretation
    Lundberg & Lee, 2017 - Link

  • Regionally Additive Models: Explainable-by-design models minimizing feature interactions
    Gkolemis Vasilis et al., 2023 - Link


License

effector is released under the MIT License.


Powered by:

explainability
iml
xai

givasile/effector

Effector - a Python package for global and regional effect methods

Jupyter Notebook

121

560 commits

updated Oct 6, 2026

See the code

README

Effector

PyPI version Execute Tests codecov Publish Documentation PyPI Downloads Code style: ruff


effector is an eXplainable AI package for tabular data. It:


📖 Documentation | 🚀 Quickstart | 🔧 API | 🏗 Examples


Installation

Effector requires Python 3.10+:

pip install effector

This installs a lightweight core (numpy, scipy, matplotlib, tqdm) that covers PDP, ALE, RHALE and their regional effects.

ShapDP needs the heavier shap/shapiq backends (which pull in numba, scikit-learn, pandas, ...). Install them only if you use that method:

pip install effector[shap]

Quickstart

(a) The inputs

A dataset as a numpy array, a model as a numpy → numpy callable, and, optionally, a schema so the explanation speaks your vocabulary:

import effector
from sklearn.ensemble import HistGradientBoostingRegressor

data = effector.datasets.BikeSharing()  # standardized numpy arrays
model = HistGradientBoostingRegressor(random_state=21).fit(data.x_train, data.y_train)
predict = effector.adapters.from_sklearn(model)  # a plain numpy -> numpy callable

schema = effector.Schema(
    feature_names=data.feature_names,
    feature_types=[
        "nominal", "nominal", "ordinal", "ordinal", "nominal", "nominal",
        "nominal", "ordinal", "continuous", "continuous", "continuous",
    ],
    category_names=[
        ["winter", "spring", "summer", "fall"],
        ["2011", "2012"],
        ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
        None,
        ["no", "yes"],
        ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
        ["no", "yes"],
        ["clear", "mist", "light rain/snow", "heavy rain"],
        None, None, None,
    ],
    scale_x_list=[
        {"mean": data.x_train_mu[i], "std": data.x_train_std[i]}
        for i in range(data.x_train.shape[1])
    ],
    scale_y={"mean": data.y_train_mu, "std": data.y_train_std},
    target_name="bike-rentals",
)

📄 In depth: the input layer; pandas, sklearn, torch, classifiers, feature types, units.

(b) The one-liner

report = effector.explain(
    data.x_train, predict, y=data.y_train, schema=schema, nof_instances=5000
)

It fits once, ranks the features, hunts for subregions where the average is hiding something, keeps only the splits that pay for themselves, and opens with the one number worth having:

[effector] global effects   (GAM)  -> 72.3% of the model's variance
           regional effects (CALM) -> 92.0%

The result is a Report, a value you can export or print:

report.to_html("report.html")  # a single self-contained page; mail it, commit it
report.show()                  # the same story, as terminal tables
  ════════════════════════════════════════════════════════════════════════
  PDP report  ·  target: bike-rentals
  ════════════════════════════════════════════════════════════════════════

  DATA & MODEL
  ────────────────────────────────────────────────────────────────────────
    instances     5,000
    features      11  ·  5 nominal · 3 ordinal · 3 continuous
    model output  mean 188 · std 176 · range [-19.5, 948]
    model R²      0.960  (on this subsample)

  EXPLAINED VARIANCE
  ────────────────────────────────────────────────────────────────────────
    step         split on                 solo     ΔR²      R²       heter
    ──────────────────────────────────────────────────────────────────────
    GAM          (all features global)       —       —   72.3%           —
  + hr           temp, workingday, yr   +18.3%  +18.3%   90.6% 0.47 → 0.26
  + temp         hr, hum                 +2.4%   +1.4%   92.0% 0.22 → 0.19
    ──────────────────────────────────────────────────────────────────────
    FINAL                                                92.0%

  REJECTED SPLITS                                            min gain 1.0%
  ────────────────────────────────────────────────────────────────────────
    feature      split on                 solo     ΔR²    reason
    ──────────────────────────────────────────────────────────────────────
  ✗ yr           hr, workingday          +2.7%   -0.8%    redundant
  ✗ hum          hr, temp                +2.2%   +0.2%    below threshold
  ✗ weekday      hr, temp, yr            +0.4%   +0.2%    below threshold
  ✗ workingday   hr, yr                  +6.2%   -4.8%    redundant

    ✗ redundant: it would explain variance on its own (see solo),
      but the accepted splits already account for it.

  FEATURES                                ranked, in the selected snapshot
  ────────────────────────────────────────────────────────────────────────
    feature        importance                          heter      #regions
    ──────────────────────────────────────────────────────────────────────
    hr                 0.7273  ██████████████████     0.2608             4
    yr                 0.2271  ██████                 0.2088             1
    temp               0.2098  █████                  0.1874             4
    hum                0.0932  ██                     0.1221             1
    ──────────────────────────────────────────────────────────────────────
    the features above carry 82% of the total importance mass

(the accepted partition trees follow)

📄 In depth: the report; the explained variance ledger, the triage plane, the regional analysis, every knob of explain(...).

(c) The interactive API

The same engine, as a live handle you query as you go:

pdp = effector.PDP(data.x_train, predict, schema=schema, nof_instances=5000)

pdp.plot("hr")                        # the global effect, ICE curves behind it
pdp.heter_score("hr")                 # 0.47: the average hides a lot
partition = pdp.find_regions("hr")    # where is it hiding it?
partition.show()
Feature 3 - Full partition tree:
🌳 Full Tree Structure:
───────────────────────
hr 🔹 [id: 0 | heter: 0.47 | inst: 5000 | w: 1.00]
    workingday = no 🔹 [id: 1 | heter: 0.33 | inst: 1545 | w: 0.31]
        temp < 6.86 🔹 [id: 2 | heter: 0.22 | inst: 778 | w: 0.16]
        temp ≥ 6.86 🔹 [id: 3 | heter: 0.26 | inst: 767 | w: 0.15]
    workingday = yes 🔹 [id: 4 | heter: 0.34 | inst: 3455 | w: 0.69]
        yr = 2011 🔹 [id: 5 | heter: 0.23 | inst: 1720 | w: 0.34]
        yr = 2012 🔹 [id: 6 | heter: 0.31 | inst: 1735 | w: 0.35]
--------------------------------------------------
Feature 3 - Statistics per tree level:
🌳 Tree Summary:
─────────────────
Level 0🔹heter: 0.47
    Level 1🔹heter: 0.34 | 🔻0.13 (28.12%)
        Level 2🔹heter: 0.26 | 🔻0.08 (22.91%)

The effect of hr depends on workingday (commute peaks vs a midday plateau), and within each branch on temp and yr. Plot the effect inside a region:

partition.plot(2)  # hr, on non-working days, in the cold
Global effect of hr with ICE curvesEffect of hr on cold non-working days

Every verb answers one question:

verbquestion it answers
.plot(f)what does feature f do?
.eval(f, xs)…as numbers, on my grid
.importance(f)how much does f move the output?
.heter_score(f)is the average hiding something?
.find_regions(f)where is it hiding it?
.select_regions()which splits actually earn their keep?
.fit(features, **cfg)(optional) tune the method first

📄 In depth: the interactive API; construct and fit, customizing .fit(), plot, eval, scores, regions.


Documentation map

Start here:

Going deeper:


Supported Methods

Every method computes global effects, and regional effects via .find_regions(feature):

MethodClassReferenceML modelSpeed
PDPPDPFriedman, 2001anyfast for a small dataset
d-PDPDerPDPGoldstein et al., 2013differentiablefast for a small dataset
ALEALEApley & Zhu, 2020anyfast
RHALERHALEGkolemis et al., 2023differentiablevery fast
SHAP-DPShapDPLundberg & Lee, 2017anyfast for a small dataset and a light model

Choosing a method

Three questions decide: is the dataset small (N < 10K) or large? Is the model light (< 0.1s per call) or heavy? Is it differentiable or not?

your caseuse
small + lightany: PDP, ALE, ShapDP; plus RHALE, DerPDP if differentiable
small + heavyPDP, ALE; plus RHALE, DerPDP if differentiable
large + differentiableRHALE
large + non-differentiableALE

Citation

If you use effector, please cite it:

@misc{gkolemis2024effector,
  title={effector: A Python package for regional explanations},
  author={Vasilis Gkolemis et al.},
  year={2024},
  eprint={2404.02629},
  archivePrefix={arXiv},
  primaryClass={cs.LG}
}

Spotlight on effector

🎤 Talks & Presentations

🌍 Adoption & Collaborations

🔍 Additional Resources

Papers that have inspired effector:

  • REPID: Regional Effects in Predictive Models
    Herbinger et al., 2022 - Link

  • Decomposing Global Feature Effects Based on Feature Interactions
    Herbinger et al., 2023 - Link

  • RHALE: Robust Heterogeneity-Aware Effects
    Gkolemis Vasilis et al., 2023 - Link

  • DALE: Decomposing Global Feature Effects
    Gkolemis Vasilis et al., 2023 - Link

  • Greedy Function Approximation: A Gradient Boosting Machine
    Friedman, 2001 - Link

  • Visualizing Predictor Effects in Black-Box Models
    Apley, 2016 - Link

  • SHAP: A Unified Approach to Model Interpretation
    Lundberg & Lee, 2017 - Link

  • Regionally Additive Models: Explainable-by-design models minimizing feature interactions
    Gkolemis Vasilis et al., 2023 - Link


License

effector is released under the MIT License.


Powered by:

explainability
iml
xai

Project health

Latest release

0.6.0 · Oct 6, 2026

Releases this year

5