Api extras
Models
effector.models
Analytic toy models with exact predict and jacobian.
Each class is a closed-form function of a few features, built to exercise one
specific phenomenon (a conditional interaction, a general interaction, a
categorical gate). Because the math is known, every effect method's output can
be checked against pen-and-paper ground truth — effector.benchmarks pairs
these models with data distributions and spells those ground truths out.
model = effector.models.ConditionalInteraction()
y = model.predict(x) # (N, 3) -> (N,)
dy = model.jacobian(x) # (N, 3) -> (N, 3)
Classes:
| Name | Description |
|---|---|
ConditionalCategorical |
|
ConditionalInteraction |
|
ConditionalInteraction4Regions |
|
DoubleConditionalInteraction |
|
GeneralInteraction |
|
ConditionalCategorical()
Bases: Base
The categorical-FOI ground-truth model (method_semantics.md tests).
\(f(x) = a_{x_0} + b_{x_0} \, x_1 \, \mathbb{1}_{x_2 > 0}\), with \(x_0 \in \{0, 1, 2\}\), \(a = [0, 1, 3]\), \(b = [1, -1, 0]\).
Every per-level quantity (PDP, ALE transitions, heterogeneity) has a closed form: the per-level PDP is \(a_k + b_k \bar{g}\) and the heterogeneity is \((b_k - \bar{b}_w)^2 \mathrm{Var}(g)\) with \(g_i = x_1^i \mathbb{1}_{x_2^i > 0}\).
Source code in effector/models.py
239 240 241 242 243 244 245 246 247 248 249 250 | |
ConditionalInteraction()
Bases: Base
The canonical conditional interaction: the sign of \(x_2\) flips the effect of \(x_1\).
\(f(x_1, x_2, x_3) = -x_1^2\mathbb{1}_{x_2 < 0} + x_1^2\mathbb{1}_{x_2 \geq 0} + e^{x_3}\)
The global effect of \(x_1\) averages out to zero while the regional
effects (\(\pm x_1^2\)) are strong — the textbook case for
find_regions, with one optimal split (\(x_2\) at 0).
Methods:
| Name | Description |
|---|---|
jacobian |
Calculate the Jacobian of the model. |
predict |
Predict. |
Source code in effector/models.py
34 35 36 37 38 39 40 41 42 43 | |
jacobian(x)
Calculate the Jacobian of the model.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
ndarray
|
Input data, shape (N, 3) |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Jacobian of the model, shape (N, 3) |
Source code in effector/models.py
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | |
predict(x)
Predict.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
ndarray
|
Input data, shape (N, 3) |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Output of the model, shape (N,) |
Source code in effector/models.py
45 46 47 48 49 50 51 52 53 54 55 56 57 58 | |
ConditionalInteraction4Regions()
Bases: Base
Two gates on the effect of \(x_1\): \(x_3\) flips its sign, \(x_2\) picks its power.
\(f(x_1, x_2, x_3, x_4) = \begin{cases} -x_1^2 + e^{x_4}, & \text{if } x_2 < 0 \text{ and } x_3 < 0 \\ x_1^2 + e^{x_4}, & \text{if } x_2 < 0 \text{ and } x_3 \geq 0 \\ -x_1^4 + e^{x_4}, & \text{if } x_2 \geq 0 \text{ and } x_3 < 0 \\ x_1^4 + e^{x_4}, & \text{if } x_2 \geq 0 \text{ and } x_3 \geq 0 \end{cases}\)
Four leaf regions with deterministic effects; the optimal split order
is dictated by the math (first \(x_3\), the sign gate, then \(x_2\), the
power gate) — see benchmarks.ConditionalInteraction4RegionsUniform.
Methods:
| Name | Description |
|---|---|
jacobian |
Calculate the Jacobian of the model. |
predict |
Predict. |
Source code in effector/models.py
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | |
jacobian(x)
Calculate the Jacobian of the model.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
ndarray
|
Input data, shape (N, 4) |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Jacobian of the model, shape (N, 4) |
Source code in effector/models.py
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 | |
predict(x)
Predict.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
ndarray
|
Input data, shape (N, 4) |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Output of the model, shape (N,) |
Source code in effector/models.py
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | |
DoubleConditionalInteraction()
Bases: Base
A two-level conditional interaction: the signs of \(x_2\) and \(x_3\) jointly gate the effect of \(x_1\).
\(f(x_1, x_2, x_3) = -3x_1^2\mathbb{1}_{x_2 < 0}\mathbb{1}_{x_3 < 0} +x_1^2\mathbb{1}_{x_2 < 0}\mathbb{1}_{x_3 \geq 0} -e^{x_1}\mathbb{1}_{x_2 \geq 0}\mathbb{1}_{x_3 < 0} +e^{3x_1}\mathbb{1}_{x_2 \geq 0}\mathbb{1}_{x_3 \geq 0}\)
Four quadrants, four different effects of \(x_1\) — resolving the heterogeneity requires a two-level partition.
Methods:
| Name | Description |
|---|---|
jacobian |
Calculate the Jacobian of the model. |
predict |
Predict. |
Source code in effector/models.py
78 79 80 81 82 83 84 85 86 87 88 89 | |
jacobian(x)
Calculate the Jacobian of the model.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
ndarray
|
Input data, shape (N, 3) |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Jacobian of the model, shape (N, 3) |
Source code in effector/models.py
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | |
predict(x)
Predict.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
ndarray
|
Input data, shape (N, 3) |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Output of the model, shape (N,) |
Source code in effector/models.py
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | |
GeneralInteraction()
Bases: Base
A smooth (non-conditional) interaction: \(x_1\) and \(x_2\) interact multiplicatively.
\(f(x_1, x_2, x_3) = x_1 x_2^2 + e^{x_3}\)
Under a zero-mean \(x_1\), the interaction is invisible in the mean
effect of \(x_2\) but shows up in its heterogeneity — no crisp region
split resolves it (contrast with ConditionalInteraction).
Methods:
| Name | Description |
|---|---|
jacobian |
Calculate the Jacobian of the model. |
predict |
Predict. |
Source code in effector/models.py
196 197 198 199 200 201 202 203 204 205 | |
jacobian(x)
Calculate the Jacobian of the model.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
ndarray
|
Input data, shape (N, 3) |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Jacobian of the model, shape (N, 3) |
Source code in effector/models.py
219 220 221 222 223 224 225 226 227 228 229 230 231 232 | |
predict(x)
Predict.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
ndarray
|
Input data, shape (N, 3) |
required |
Returns:
| Type | Description |
|---|---|
ndarray
|
Output of the model, shape (N,) |
Source code in effector/models.py
207 208 209 210 211 212 213 214 215 216 217 | |
Datasets
effector.datasets
Data generators and real datasets used in the examples and tests.
Synthetic generators (IndependentUniform) produce numpy arrays with known
distributions — pair them with effector.models for ground-truth checks.
Real datasets (BikeSharing, MedicalCosts, AirfoilSelfNoise,
AdultIncome) fetch, split, and optionally standardize a public
dataset into ready-to-use x_train / y_train / x_test / y_test numpy
arrays. Datasets with categorical columns also expose feature_types and
category_names, ready to feed effector.Schema.
Classes:
| Name | Description |
|---|---|
AdultIncome |
The Adult (census income) dataset (UCI id 2) — a classification |
AirfoilSelfNoise |
The NASA Airfoil Self-Noise dataset (UCI id 291). |
Base |
|
BikeSharing |
The UCI Bike Sharing dataset (hourly) — effector's canonical real example. |
IndependentUniform |
|
MedicalCosts |
The Medical Cost Personal dataset — annual medical charges billed by |
RealDatasetBase |
Shared plumbing for real datasets: fetch, seeded train/test split, |
AdultIncome(pcg_train=0.8, standardize=False, seed=21)
Bases: RealDatasetBase
The Adult (census income) dataset (UCI id 2) — a classification
example: explain predict_proba of the positive class.
45,222 census records after dropping rows with missing values; the
target is binary — whether yearly income exceeds $50K. 12 features
remain after dropping fnlwgt (a sampling weight) and education
(duplicated by education-num). Categorical columns are encoded to
integer codes with the level names recorded in category_names; levels
rarer than 50 rows are bucketed into "Other" (a level that rare can
vanish from a train split, invalidating the schema). Kept in natural
units by default (standardize=False); the 0/1 target is never a
regression target — pair it with a classifier and explain the predicted
probability.
data = effector.datasets.AdultIncome()
data.x_train, data.y_train # (36177, 12), (36177,) with y in {0, 1}
Fetch and prepare the dataset.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pcg_train
|
Fraction of samples in the train split. |
0.8
|
|
standardize
|
Standardize the features (default False — natural units keep the partition rules readable). The 0/1 target is standardized too when True; leave False for classification. |
False
|
|
seed
|
int
|
Random seed of the train/test shuffle. |
21
|
Source code in effector/datasets.py
370 371 372 373 374 375 376 377 378 379 380 381 382 | |
AirfoilSelfNoise(pcg_train=0.8, standardize=False, seed=21)
Bases: RealDatasetBase
The NASA Airfoil Self-Noise dataset (UCI id 291).
1,503 wind-tunnel measurements of NACA 0012 airfoil sections; the target
is the scaled sound pressure level in dB. Five continuous features:
frequency (Hz), angle of attack (deg), chord length (m), free-stream
velocity (m/s), suction-side displacement thickness (m). Kept in natural
units by default (standardize=False).
data = effector.datasets.AirfoilSelfNoise()
data.x_train, data.y_train # (1202, 5), (1202,)
Fetch and prepare the dataset.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pcg_train
|
Fraction of samples in the train split. |
0.8
|
|
standardize
|
Standardize features and target (default False — natural units keep the partition rules readable). |
False
|
|
seed
|
int
|
Random seed of the train/test shuffle. |
21
|
Source code in effector/datasets.py
314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 | |
Base(name, dim, axis_limits)
Methods:
| Name | Description |
|---|---|
generate_data |
Generate |
Source code in effector/datasets.py
18 19 20 21 | |
generate_data(n, seed=21)
Generate n samples.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n
|
int
|
Number of samples. |
required |
seed
|
int
|
Random seed, for reproducibility. |
21
|
Returns:
| Type | Description |
|---|---|
ndarray
|
The samples, shape |
Source code in effector/datasets.py
23 24 25 26 27 28 29 30 31 32 33 | |
BikeSharing(pcg_train=0.8, standardize=True, seed=21)
Bases: RealDatasetBase
The UCI Bike Sharing dataset (hourly) — effector's canonical real example.
17,379 hourly records of the Capital Bikeshare system; the target is the
hourly rental count. Fetched via ucimlrepo (UCI id 275), with dteday
and atemp dropped — 11 features remain (season, yr, mnth, hr, holiday,
weekday, workingday, weathersit, temp, hum, windspeed). Data is split into
train/test with a seeded shuffle and (by default) standardized; the UCI
normalization constants of temp/hum/windspeed are folded into the stored
mu/std, so scale_x/scale_y map plots back to natural units.
data = effector.datasets.BikeSharing()
data.x_train, data.y_train # (13903, 11), (13903,)
data.feature_names, data.target_name
Fetch and prepare the dataset.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pcg_train
|
Fraction of samples in the train split. |
0.8
|
|
standardize
|
Standardize features and target to zero mean, unit variance (the mu/std used are stored on the object). |
True
|
|
seed
|
int
|
Random seed of the train/test shuffle. |
21
|
Source code in effector/datasets.py
171 172 173 174 175 176 177 178 179 180 181 182 | |
IndependentUniform(dim=2, low=0, high=1)
Bases: Base
dim independent features, each uniform on [low, high].
The simplest possible distribution — no correlations, flat marginals — so any structure in an effect plot comes from the model alone. The default data source of the synthetic benchmarks.
Initialize the generator.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dim
|
int
|
Number of features. |
2
|
low
|
float
|
Lower bound of every feature. |
0
|
high
|
float
|
Upper bound of every feature. |
1
|
Methods:
| Name | Description |
|---|---|
generate_data |
Generate |
Source code in effector/datasets.py
44 45 46 47 48 49 50 51 52 53 | |
generate_data(n, seed=21)
Generate n samples.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n
|
int
|
Number of samples. |
required |
seed
|
int
|
Random seed, for reproducibility. |
21
|
Returns:
| Type | Description |
|---|---|
ndarray
|
The samples, shape |
Source code in effector/datasets.py
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | |
MedicalCosts(pcg_train=0.8, standardize=False, seed=21)
Bases: RealDatasetBase
The Medical Cost Personal dataset — annual medical charges billed by an insurer, the textbook smoker × bmi interaction.
1,338 policyholders with 6 features (age, sex, bmi, children, smoker,
region); the target is the individual's yearly medical charges in USD.
Fetched from the dataset's canonical mirror (stedy/Machine-Learning-with-R-datasets).
Categorical columns are encoded to integer codes; feature_types and
category_names are populated for effector.Schema. Kept in natural
units by default (standardize=False) so partition rules read directly
(e.g. bmi < 30).
data = effector.datasets.MedicalCosts()
data.x_train, data.y_train # (1070, 6), (1070,)
schema = effector.Schema(
feature_names=data.feature_names,
feature_types=data.feature_types,
category_names=data.category_names,
target_name=data.target_name,
)
Fetch and prepare the dataset.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
pcg_train
|
Fraction of samples in the train split. |
0.8
|
|
standardize
|
Standardize features and target (default False — natural units keep the partition rules readable). |
False
|
|
seed
|
int
|
Random seed of the train/test shuffle. |
21
|
Source code in effector/datasets.py
246 247 248 249 250 251 252 253 254 255 256 257 | |
RealDatasetBase(name, pcg_train, standardize, seed=21)
Shared plumbing for real datasets: fetch, seeded train/test split, optional standardization (storing the per-feature mu/std for mapping plots back to natural units).
Source code in effector/datasets.py
78 79 80 81 82 83 84 85 86 87 88 89 90 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 | |
Benchmarks
effector.benchmarks
Synthetic (model, distribution) pairs with closed-form effect ground truths.
A feature-effect ground truth is a property of the pair (prediction function,
data distribution) — never of the model alone: the same model under a different
distribution has different PDP/ALE/SHAP curves. Each class below bundles one such
pair and exposes its closed-form effects, so the class name states the exact
scope of validity of every *_gt method.
The derivations live in notebooks/synthetic-examples/ (05, 06, 07) and, for
CorrelatedInteraction, in the RHALE paper (https://arxiv.org/abs/2309.11193);
the notebooks and tests/test_functional_*.py both consume the functions
defined here, so the two can never disagree about the right answer.
All *_gt(feature, xs) methods return the effect centered to zero integral
over the feature's axis range (matching eval(..., centering=True)), unless
stated otherwise in their docstring.
Classes:
| Name | Description |
|---|---|
ConditionalInteraction4RegionsUniform |
|
ConditionalInteractionUniform |
|
CorrelatedInteraction |
The Gkolemis et al. 2023 (RHALE paper) example — correlated features. |
GeneralInteractionUniform |
|
ConditionalInteraction4RegionsUniform()
models.ConditionalInteraction4Regions under iid U[-1, 1]^4.
\(f(x) = \pm x_1^2 / \pm x_1^4\) gated by the signs of \(x_2, x_3\), plus \(e^{x_4}\).
Derivations: notebooks/synthetic-examples/07_conditional_interaction_4_regions_*.ipynb
Methods:
| Name | Description |
|---|---|
regional_effect_gt |
Centered effect of x1 inside each of the 4 leaf regions. |
Source code in effector/benchmarks.py
200 201 202 203 | |
regional_effect_gt(x2_side, x3_side, xs)
Centered effect of x1 inside each of the 4 leaf regions.
Source code in effector/benchmarks.py
249 250 251 252 253 254 255 256 257 258 259 | |
ConditionalInteractionUniform()
models.ConditionalInteraction under iid U[-1, 1]^3.
\(f(x) = -x_1^2 1_{x_2 < 0} + x_1^2 1_{x_2 \geq 0} + e^{x_3}\)
Derivations: notebooks/synthetic-examples/05_conditional_interaction_*.ipynb
Methods:
| Name | Description |
|---|---|
ale_bin_variance_gt |
Per-bin variance of the ALE local effects, Fixed(nof_bins) on [-1, 1]. |
pdp_heter_gt |
Variance of the centered ICE curves at each grid point. |
regional_effect_gt |
Centered effect of x1 inside each region of the optimal split. |
rhale_bin_variance_gt |
Per-bin variance of the derivative effects, Fixed(nof_bins) on [-1, 1]. |
Source code in effector/benchmarks.py
39 40 41 42 | |
ale_bin_variance_gt(feature, nof_bins=31)
Per-bin variance of the ALE local effects, Fixed(nof_bins) on [-1, 1].
The central bin of feature 1 (the one containing the jump at 0) is NaN: its variance is an artifact of the discontinuity, not a ground truth.
Source code in effector/benchmarks.py
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | |
pdp_heter_gt(feature, xs)
Variance of the centered ICE curves at each grid point.
Source code in effector/benchmarks.py
78 79 80 81 82 83 84 85 | |
regional_effect_gt(side, xs)
Centered effect of x1 inside each region of the optimal split.
side: "left" (x2 < 0) -> -x1^2, "right" (x2 >= 0) -> +x1^2. Per-region heterogeneity is 0: the effect is deterministic within a region.
Source code in effector/benchmarks.py
120 121 122 123 124 125 126 127 128 129 130 131 | |
rhale_bin_variance_gt(feature, nof_bins=31)
Per-bin variance of the derivative effects, Fixed(nof_bins) on [-1, 1].
df/dx1 = -+2 x1 with sign(x2) equiprobable -> variance 4 c_k^2 at the bin center; df/dx2 = 0 a.e. (no NaN bin here, unlike the ALE version: the derivative never sees the jump); df/dx3 = e^{x3} is deterministic.
Source code in effector/benchmarks.py
103 104 105 106 107 108 109 110 111 112 113 | |
CorrelatedInteraction()
The Gkolemis et al. 2023 (RHALE paper) example — correlated features.
\(f(x) = \sin(2\pi x_1) (1_{x_1 < 0} - 2 \cdot 1_{x_3 < 0}) + x_1 x_2 + x_2\)
with \(x_1\) mixture-uniform on [-0.5, 0.5] (5/6 of the mass below 0), \(x_2 \sim N(0, 2)\) and \(x_3 = x_1 + N(0, 0.01)\) — strongly correlated.
The only pair where PDP, ALE, RHALE and SHAP provably differ; each
*_gt is the correct answer for that method's own definition.
Derivations: Gkolemis et al. 2023, https://arxiv.org/abs/2309.11193
Methods:
| Name | Description |
|---|---|
ale_gt |
ALE conditions on x3 ~ x1, so the -2 sin term only acts where x1 < 0 |
d_pdp_gt |
Derivative-PDP (uncentered): E over the marginal of df/dx1. |
generate_data |
Mixture sampler: 5/6 of x1 mass on [-0.5, 0], 1/6 on [0, 0.5]. |
pdp_gt |
PDP averages over the marginal of x3, ignoring its correlation |
rhale_heter_gt |
Effect-space heterogeneity band (std) accumulated from the x2 noise |
shap_gt |
Mean SHAP curve for x1 as derived in notebook 02: -5/6 sin(2 pi x). |
Source code in effector/benchmarks.py
280 281 | |
ale_gt(xs)
ALE conditions on x3 ~ x1, so the -2 sin term only acts where x1 < 0 ... and there it flips the sign: sin - 2 sin = -sin.
Source code in effector/benchmarks.py
342 343 344 345 346 347 348 349 | |
d_pdp_gt(xs)
Derivative-PDP (uncentered): E over the marginal of df/dx1.
df/dx1 = 2 pi cos(2 pi x1) 1{x1<0} - 4 pi cos(2 pi x1) 1{x3<0} + x2, and E[1{x3<0}] = 5/6, E[x2] = 0. (Notebook 02's gt_d_pdp carries a spurious +1 — a slip of dy/dx2 = x1 + 1; fixed here.)
Source code in effector/benchmarks.py
332 333 334 335 336 337 338 339 340 | |
generate_data(n, seed=21)
Mixture sampler: 5/6 of x1 mass on [-0.5, 0], 1/6 on [0, 0.5].
Source code in effector/benchmarks.py
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 | |
pdp_gt(xs)
PDP averages over the marginal of x3, ignoring its correlation with x1: the -2 sin term contributes with probability 5/6 everywhere.
Source code in effector/benchmarks.py
323 324 325 326 327 328 329 330 | |
rhale_heter_gt(xs)
Effect-space heterogeneity band (std) accumulated from the x2 noise in df/dx1 — what notebook 02 draws around the RHALE curve.
Not asserted anywhere yet: RHALE.eval(heterogeneity=True) returns a
derivative-space per-bin std, which is a different quantity. Wire this
in once the heterogeneity payload API lands (LOGBOOK #4).
Source code in effector/benchmarks.py
354 355 356 357 358 359 360 361 | |
shap_gt(xs)
Mean SHAP curve for x1 as derived in notebook 02: -5/6 sin(2 pi x).
DISPUTED (2026-07-03): this does not match interventional Shapley — an exact brute-force computation (all coalitions, marginal imputation over the joint background) and shap's PermutationExplainer agree with each other and both sit ~0.29 away from this curve, independent of N. The notebook's derivation needs revisiting; until then nothing asserts against this function (test_functional_correlated_features.py::test_shap is skipped).
Source code in effector/benchmarks.py
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 | |
GeneralInteractionUniform()
models.GeneralInteraction under iid U[-1, 1]^3.
\(f(x) = x_1 x_2^2 + e^{x_3}\)
Derivations: notebooks/synthetic-examples/06_general_interaction_*.ipynb
Methods:
| Name | Description |
|---|---|
pdp_heter_gt |
Variance of the centered ICE curves at each grid point. |
Source code in effector/benchmarks.py
144 145 146 147 | |
pdp_heter_gt(feature, xs)
Variance of the centered ICE curves at each grid point.
Feature 1 is the reason this pair exists: the x1*x2^2 interaction is invisible in the mean effect (E[x1] = 0) but not in the heterogeneity.
Source code in effector/benchmarks.py
176 177 178 179 180 181 182 183 184 185 186 187 | |