calm-additive
CALM: interpretable by design. Accurate locally additive models for tabular data.
calm-additive fits CALMs (Conditionally Additive Local Models). It:
- gives every feature a plain 1D curve, like a GAM, so the model can be read plot by plot
- lets a feature have several curves, each valid in a region of the other features, which is how it captures interactions a GAM misses
- finds those regions itself, from a black-box teacher, and keeps only the ones that earn their complexity
- explains itself: a model card, the curves, and every prediction as a sum you can read line by line
- shows its reasoning: a ledger of every split it kept and every split it rejected
- has a scikit-learn API:
fit,predict, and keyword arguments for the knobs
π Guides | π Quickstart | π§ API | π Examples
Installation
calm-additive requires Python 3.10+.
pip install calm-additive
Quickstart
A synthetic problem with one interaction: the effect of x3 is a sine when
x2 >= 0 and a cosine when x2 < 0.
import numpy as np
import pandas as pd
from calm_additive import CALMRegressor
rng = np.random.default_rng(0)
X = pd.DataFrame(rng.uniform(-1, 1, (3000, 3)), columns=["x1", "x2", "x3"])
y = (
X["x1"] ** 2
+ np.log(np.abs(X["x2"]))
+ 2 * np.where(X["x2"] >= 0, np.sin(np.pi / 2 * X["x3"]), np.cos(np.pi / 2 * X["x3"]))
+ rng.normal(0, 0.3, len(X))
)
calm = CALMRegressor(max_partition_depth=1).fit(X, y)
calm.print_summary() # the model on one screen
calm.plot_curves() # one panel per feature, one curve per region
calm.print_prediction(X.iloc[0]) # why this prediction
calm.chain_.show() # why each split was kept or rejected
CALM finds the split and fits one curve per side:
On a held-out fifth of the data (calm.print_summary(X_test, y_test) prints this
comparison):
| model | test RΒ² |
|---|---|
| GAM (EBM, no interactions) | 0.717 |
| CALM | 0.950 |
| black box (XGBoost, the teacher) | 0.939 |
π In depth: the quickstart guide walks through every line and its output.
The knobs
| knob | what it decides |
|---|---|
max_conditional_interactions |
the most conditional interactions the model may have |
max_partition_depth |
how many curves a feature may have: at most 2**max_partition_depth |
min_heterogeneity_drop |
how much a split must reduce a feature's heterogeneity to be made |
min_r2_gain |
how much a feature's partition must explain to be kept |
teacher |
the black box the regions are learned from |
effect |
how the teacher is read: PDP, ALE, SHAP-DP or RHALE |
partition_features |
which features may get a partition at all |
curve_fitter |
what is fitted inside the regions |
π In depth: choosing the knobs.
Documentation map
- Quickstart: the synthetic problem, line by line
- A real dataset: Bike Sharing, from a DataFrame to the curves and one prediction
- How it works: teacher, regions, selection, refit
- Choosing the knobs: your case, what to set
- Examples: four runnable scripts
- API Docs: the reference
Citation
@inproceedings{gkolemis2026calm,
title = {Interpretability-by-Design with Accurate Locally Additive Models
and Conditional Feature Effects},
author = {Gkolemis, Vasilis and Kavouras, Loukas and Kyriakopoulos, Dimitrios
and Tsopelas, Konstantinos and Rontogiannis, Dimitrios
and Casalicchio, Giuseppe and Dalamagas, Theodore and Diou, Christos},
booktitle = {Advances in Neural Information Processing Systems (NeurIPS)},
year = {2026}
}
Built on
effector, for reading the black box; InterpretML, for the EBM fitted inside the regions; XGBoost, for the default teacher.
Acknowledgments
The work leading to these results has been funded by the European Union under Grant Agreement No. 101289094 (project AIRIS). Views and opinions expressed are however those of the authors and do not necessarily reflect those of the European Union or the granting authority (HaDEA). Neither the European Union nor the granting authority can be held responsible for them.
The research leading to this work has received funding from the European Unionβs Horizon Europe research and innovation program under Grant Agreement No: 101135826 (aidapt.eu).
