FlagGAM: rule-basis generalized additive models for explainable tabular prediction (Zhao & Welsch, 2026; from-scratch implementation).
Project description
FlagGAM
Documentation & guidebook: https://wlazlod.github.io/flaggam/ (available once the repository is public).
FlagGAM is a method for building interpretable generalized additive models from tabular data. It works by first running a univariate screening pass — the Univariate Flagging Algorithm (Sheth et al., 2019) — that identifies threshold and category cuts where a feature's distribution shifts meaningfully relative to the outcome. Each surviving cut becomes a binary basis function ("flag"), and the flags together form a compact, human-readable rule basis.
A linear or logistic head is then fitted on top of the rule basis, producing a model whose predictions are sums of individually interpretable flag contributions. Because every flag corresponds to a concrete data condition (e.g., "age ≥ 55"), the resulting model supports exact rule extraction and feature-level attribution without approximation.
This package is a from-scratch Python implementation of FlagGAM (Zhao & Welsch, arXiv:2605.31189)
and the Univariate Flagging Algorithm (Sheth et al., PLOS ONE 2019). It provides
sklearn-compatible FlagGAMClassifier and FlagGAMRegressor estimators that integrate
directly into standard scikit-learn pipelines.
Installation
# Editable install (development)
pip install -e .
# Or with uv
uv sync --extra dev
Quickstart
Rule discovery requires enough rows per tail (min_support). The example below
uses 600 synthetic rows with a planted signal so that export_rules() returns
non-trivial rules.
import numpy as np
import pandas as pd
from flaggam import FlagGAMClassifier
rng = np.random.default_rng(0)
n = 600
age = rng.normal(40, 10, n)
purpose = rng.choice(["car", "tv", "edu"], n)
logit = -1.5 + 2.0 * (age <= 30) + 1.5 * (purpose == "edu")
y = (rng.uniform(size=n) < 1 / (1 + np.exp(-logit))).astype(int)
X = pd.DataFrame({"age": age, "purpose": pd.Categorical(purpose)})
clf = FlagGAMClassifier(random_state=0).fit(X, y)
# Inspect the rule basis (5 rules on this seed)
rules = clf.export_rules()
print(rules[["feature", "rule", "weight"]])
# feature rule weight
# age age <= 27.4074 1.589593
# age age >= 46.7581 -0.387614
# purpose purpose == 'edu' 0.906901
# purpose purpose == 'tv' -0.418348
# purpose purpose == 'car' -0.486362
# Attribution for a young 'edu' applicant
x_young = pd.DataFrame({"age": [22.0], "purpose": pd.Categorical(["edu"])})
explanation = clf.explain(x_young)
print(explanation)
# row feature rule value contribution
# 0 age age <= 27.4074 1.0 1.589593
# 0 purpose purpose == 'edu' 1.0 0.906901
# 0 <intercept> <intercept> 1.0 -0.624096
Extensions (beyond the paper)
Three optional modules extend the paper's method; each is an original addition not present in Zhao & Welsch (2026) and lives in its own module.
PD calibration — diagnostics (reliability curve, Brier, ECE,
calibration-in-the-large) and recalibration (platt, isotonic,
base_rate) fitted on data disjoint from head fitting:
from flaggam import CalibratedFlagGAM, expected_calibration_error
cal = CalibratedFlagGAM(FlagGAMClassifier(random_state=0), method="platt", cv=5)
cal.fit(X, y)
pd_hat = cal.predict_proba(X)[:, 1]
Monotonicity constraints — regulators often require PD monotone in a feature. Because FlagGAM's numerical contributions are step/ramp bases, sign constraints give exact monotonicity:
clf_mono = FlagGAMClassifier(monotonic_constraints={"age": -1}).fit(X, y) # PD non-increasing in age
Fairness / proxy audit — group metrics for a protected attribute and a rule-level audit that ranks bases by association with it:
from flaggam import ProxyAudit, group_metrics
A = X["purpose"].astype(str) # protected attribute (illustrative)
metrics = group_metrics(y, clf.predict_proba(X)[:, 1], A)
report = ProxyAudit(clf).report(X, A) # ranked candidate proxies
clean_clf, trade = ProxyAudit(clf).drop_proxies(X, y, A, threshold=0.3)
Benchmarks
Reproducing the paper's tables requires the benchmarks optional dependency group:
uv sync --extra benchmarks
Each runner produces one paper table as a tidy results CSV:
python -m benchmarks.run_classification # Table 3 (classification AUROC)
python -m benchmarks.run_regression # Table 4 (regression RMSE/R2)
python -m benchmarks.run_robustness # Table 5 (missingness/noise robustness)
python -m benchmarks.run_ablation # Table 7 (FlagGAM ablations)
python -m benchmarks.run_sensitivity # Table 8 (hyperparameter sensitivity)
All runners default to --n-splits 1000, matching the paper, which takes hours per table.
Pass --n-splits 25 for a quick pass while developing or sanity-checking a change.
Rows are always APPENDED to --out if it already exists (this supports chunked
--seed-start resumption); delete the file first if you want a fresh run.
python -m benchmarks.render_tables benchmarks/results/classification.csv --table 3
render_tables.py compares a results CSV against the paper's reported values
(Zhao & Welsch, arXiv:2605.31189, benchmarks/paper_targets.py) and flags deltas beyond
tolerance. Results CSVs are written under benchmarks/results/ and are gitignored — they are
run artifacts, not tracked outputs.
Citation
If you use this package in research, please cite the papers it implements:
FlagGAM method:
Zhao, Z. & Welsch, R. E. (2026).
FlagGAM: Rule-Basis Generalized Additive Models for Explainable Tabular Prediction.
arXiv:2605.31189.
Univariate Flagging Algorithm:
Sheth, M., Gerovitch, A., Welsch, R. E., Markuzon, N. (2019).
The Univariate Flagging Algorithm (UFA): An interpretable approach for predictive modeling.
PLOS ONE 14(10): e0223161.
https://doi.org/10.1371/journal.pone.0223161
A machine-readable citation file is available at CITATION.cff.
Development
uv sync --extra dev
uv run pytest -q
uv run ruff check .
uv run mypy
uv run mkdocs serve # Preview documentation locally
Status
Feature-complete at 0.1.0: core estimators pass check_estimator with no exclusions; the
benchmark suite reproduces the paper's protocol (a 25-split German Credit smoke run
reproduces the paper's 0.775 AUROC to within 0.002 (gate tolerance ±0.02)); calibration,
monotonicity, fairness, and visualization
extensions are implemented and documented. See CHANGELOG.md.
License
Apache 2.0 — see LICENSE. See docs/LICENSING.md for notes
on paper copyrights and dataset licences.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file flaggam-0.1.1.tar.gz.
File metadata
- Download URL: flaggam-0.1.1.tar.gz
- Upload date:
- Size: 616.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d37d18213d0e2b037ca1da6caba2ef30df567f0809e7d75d6ea5c1cb0b50239d
|
|
| MD5 |
c2061e04565f3286f1eaf08ad64fb3cf
|
|
| BLAKE2b-256 |
6e9846623ee2a4a7b6b5b21549b4c81b1ebccdec9dd1d3fc0a3eb287c02b4eac
|
Provenance
The following attestation bundles were made for flaggam-0.1.1.tar.gz:
Publisher:
publish.yml on wlazlod/flaggam
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
flaggam-0.1.1.tar.gz -
Subject digest:
d37d18213d0e2b037ca1da6caba2ef30df567f0809e7d75d6ea5c1cb0b50239d - Sigstore transparency entry: 2112998216
- Sigstore integration time:
-
Permalink:
wlazlod/flaggam@6fcc9639c34ed0ce11b64188c47b17b6c40dd444 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/wlazlod
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6fcc9639c34ed0ce11b64188c47b17b6c40dd444 -
Trigger Event:
push
-
Statement type:
File details
Details for the file flaggam-0.1.1-py3-none-any.whl.
File metadata
- Download URL: flaggam-0.1.1-py3-none-any.whl
- Upload date:
- Size: 43.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
003ccb1ad82f66477121cce067cc17a8d656b82e03152aeae922b8b6ab7f0473
|
|
| MD5 |
ffd910638accb1b9d8c2192f4c168500
|
|
| BLAKE2b-256 |
7f1969484684709a36ddbbead9ff05ec02094ef63b4132d1c9cb23d2edf75e1a
|
Provenance
The following attestation bundles were made for flaggam-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on wlazlod/flaggam
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
flaggam-0.1.1-py3-none-any.whl -
Subject digest:
003ccb1ad82f66477121cce067cc17a8d656b82e03152aeae922b8b6ab7f0473 - Sigstore transparency entry: 2112998285
- Sigstore integration time:
-
Permalink:
wlazlod/flaggam@6fcc9639c34ed0ce11b64188c47b17b6c40dd444 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/wlazlod
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6fcc9639c34ed0ce11b64188c47b17b6c40dd444 -
Trigger Event:
push
-
Statement type: