SHAP-independent D'Hondt-projected removal-effect attributions for tabular XAI
Project description
DhondtXAI
DhondtXAI is a SHAP-independent, D'Hondt-based post-hoc attribution library for tabular models. It does not compute SHAP values or approximate Shapley values. Instead, it defines a separate D'Hondt-projected removal-effect attribution operator. SHAP can still be used as an external benchmark.
Status: DhondtXAI 0.9.1 is an experimental/beta tabular XAI library. It is suitable for research, model inspection, and controlled pilot use. For high-stakes deployment, validate explanations against task-specific benchmarks and compare them with established methods such as SHAP and LIME.
Install
After the package is published to PyPI:
pip install dhondtxai
For local development from this repository:
pip install -e .[dev]
scikit-learn is optional for the core library. Install .[sklearn] or .[dev]
when you want to run the included sklearn examples and tests.
Quick Start
import pandas as pd
from sklearn.datasets import load_breast_cancer
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from dhondtxai import DhondtXAI
dataset = load_breast_cancer()
X = pd.DataFrame(dataset.data, columns=dataset.feature_names)
y = pd.Series(dataset.target)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42, stratify=y
)
model = RandomForestClassifier(n_estimators=200, random_state=42)
explainer = DhondtXAI(
model,
background_data=X_train,
output_type="probability",
class_index=1,
)
explanation = explainer.explain(
X_test.iloc[0],
threshold=0.05,
redistribute=True,
n_background=50,
)
print(explanation.summary())
print(explanation.to_feature_frame(top_k=10))
explainer.plot_waterfall(explanation, top_k=10)
Method Summary
For a trained model score g_c(x), DhondtXAI computes a baseline
mu_c = E[g_c(X)]
and the local model difference
Delta_c(x) = g_c(x) - mu_c
For a feature or feature alliance A, the method estimates a
background-interventional removal score
R_A^D(x) = (1 / M) sum_m g_c(x_-A, z_A^(m))
and the local removal effect
e_A^D(x) = g_c(x) - R_A^D(x)
The samples z_A^(m) come from the background data provided through fit(...)
or background_data. This is not a Shapley-value or SHAP computation.
These effects are converted into positive and negative explanatory votes. The D'Hondt rule allocates explanatory seats separately for supporting and opposing evidence. Signed source back-projection keeps positive and negative source effects separate. A final conservative projection maps the seat-based representation back onto the model difference:
sum_i phi_i^D(x) = g_c(x) - mu_c
DhondtXAI therefore produces signed local attributions in a SHAP-like additive format, but the values should be interpreted as D'Hondt-projected removal-effect attributions rather than Shapley values.
Main Features
- SHAP-independent local feature attributions.
- Background-interventional feature removal using a background dataset.
- Optional conditional KNN perturbation for more local replacements.
- Manual, automatic, hybrid, or no feature alliances.
- Same-direction or absolute-interaction affinity for automatic alliances.
- Optional threshold/barrier mechanism.
- Optional redistribution of below-threshold alliance votes.
- Positive and negative D'Hondt evidence parliaments.
- Explicit stable or random D'Hondt tie-breaking.
- Excluded-feature and below-threshold residual reporting.
- Projection residual diagnostics.
- Separate attribution resolution and display seat counts.
- Local and global explanation outputs.
- Global alliance co-occurrence matrix for automatic/hybrid alliances.
- Backward-compatible legacy
feature_importances_allocation API.
What DhondtXAI Is Not
DhondtXAI is not SHAP. It does not estimate Shapley values, does not average marginal contributions over all feature coalitions, and its explanations should not be interpreted as Shapley values. It is a separate D'Hondt-based attribution operator that can be compared with SHAP in experiments.
When To Use
Use DhondtXAI when you want signed local feature attributions, alliance-level explanations, threshold/barrier analysis, parliamentary representation of model evidence, and global alliance co-occurrence analysis.
Local Explanation Example
import pandas as pd
from sklearn.datasets import load_breast_cancer
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from dhondtxai import DhondtXAI, plot_signed_parliament
dataset = load_breast_cancer()
X = pd.DataFrame(dataset.data, columns=dataset.feature_names)
y = pd.Series(dataset.target)
X_train, X_test, y_train, y_test = train_test_split(
X,
y,
test_size=0.3,
random_state=42,
stratify=y,
)
model = RandomForestClassifier(n_estimators=200, random_state=42)
explainer = DhondtXAI(model, output_type="probability", class_index=1)
explainer.fit(X_train, y_train)
explanation = explainer.explain(
X_test.iloc[0],
seats=120,
allocation_seats=5000,
threshold=0.05,
redistribute=True,
alliance_mode="user",
user_alliances=[
["mean concavity", "mean concave points"],
["mean radius", "mean perimeter", "mean area"],
],
n_background=50,
lambda_interaction=0.2,
)
print(explanation.to_feature_frame())
print(explanation.to_alliance_frame())
print(explanation.summary(top_k=5))
print(explanation.diagnostics())
print(explanation.projection_residual_ratio)
explainer.plot_local_bar(explanation, top_k=10)
explainer.plot_waterfall(explanation, top_k=10)
plot_signed_parliament(explanation, mode="signed")
Model Compatibility
DhondtXAI can explain any tabular model that maps input rows to numeric scores. It does not require model internals, gradients, tree structure, or SHAP values.
Supported scoring mechanisms:
- sklearn-style
predict_proba - sklearn-style
decision_function - numeric
predict - custom callable
predict_fn
For already-trained models, you can pass the background data directly:
explainer = DhondtXAI(
trained_model,
background_data=X_train,
output_type="probability",
class_index=1,
)
For custom, Keras, PyTorch, ONNX, or remote models, pass a callable:
def predict_fn(X):
return my_model_score_function(X)
explainer = DhondtXAI(
predict_fn=predict_fn,
background_data=X_train,
output_type="custom",
)
If your model expects NumPy arrays instead of pandas DataFrames:
explainer = DhondtXAI(
predict_fn=lambda X: keras_model.predict(X, verbose=0)[:, 1],
background_data=X_train,
output_type="custom",
input_format="numpy",
)
For more complex conversions, use input_adapter:
explainer = DhondtXAI(
predict_fn=predict_fn,
background_data=X_train,
output_type="custom",
input_adapter=lambda X: X.to_numpy(dtype="float32"),
)
For multi-output regression or custom score matrices, select the target column:
explainer = DhondtXAI(
model,
background_data=X_train,
output_type="prediction",
target_index=1,
)
For multiclass classification, you may explain the predicted class directly:
explanation = explainer.explain(
X_test.iloc[0],
class_index="predicted",
)
Check compatibility before running a large explanation job:
print(explainer.check_model_compatibility())
If no background data has been set yet, pass a sample directly:
print(explainer.check_model_compatibility(X_sample=X_train.head()))
Alliance Modes
alliance_mode="none" treats each feature as its own actor.
alliance_mode="user" uses only user-defined disjoint alliances and keeps
remaining features as individual actors.
alliance_mode="auto" estimates pairwise interaction affinity and forms
automatic alliances. Use auto_alliance_method="connected_components" for the
default graph component rule or auto_alliance_method="complete_linkage" for a
stricter rule requiring all pairs inside an alliance to meet the affinity
threshold.
alliance_mode="hybrid" preserves user-defined alliances and applies automatic
alliance formation only to the remaining features.
Threshold And Redistribution
Set threshold=None or threshold_enabled=False to disable the barrier system.
Set threshold=0.05 to require at least 5 percent of the explanatory vote.
If redistribute=True, below-threshold alliance votes are transferred to
eligible alliances according to affinity. If redistribute=False, below-threshold
alliances are reported but do not receive seats. Their model contribution is
kept in __below_threshold__ and explanation.below_threshold_residual instead
of being forced into eligible features.
If exclude_features=[...] is used, excluded feature influence is not assigned
to the remaining active features. It is reported through __excluded__ and
explanation.excluded_residual.
Attribution Resolution And Display Seats
allocation_seats controls the numerical D'Hondt resolution used to compute
continuous attributions. Larger values reduce integer seat rounding effects.
seats controls the visible parliament size used in the plotted explanation.
For example, allocation_seats=10000 and seats=100 produces a high-resolution
attribution with a compact 100-seat visualization.
If allocation_seats is not provided, DhondtXAI uses:
max(5000, 100 * number_of_active_features, seats)
This default prevents small display parliaments such as seats=10 from
zeroing out meaningful low-rank features in the numerical attribution.
Perturbation, Affinity, And Tie-Breaking
perturbation="interventional" is the default. It replaces the removed feature
or feature group with values sampled from the background data:
g_c(x_-A, z_A)
perturbation="conditional_knn" uses nearest background rows according to the
non-removed features before taking replacement values. This is still an
approximation, but it reduces unrealistic replacements when correlated tabular
features are present.
For domain-specific replacements, use perturbation="user_sampler":
def sampler(x, group, background, n):
rows = background.sample(n=n, replace=True, random_state=42).reset_index(drop=True)
# edit rows[group] here using domain-specific rules
return rows
explainer = DhondtXAI(
model,
background_data=X_train,
perturbation="user_sampler",
perturbation_sampler=sampler,
)
Automatic alliances can use:
affinity_mode="same_direction": only same-sign single-feature effects can form high-affinity alliances.affinity_mode="absolute_interaction": pairwise interaction magnitude can create affinity even when single-feature effects are weak or opposite.
D'Hondt ties are explicit:
tie_break="stable": deterministic order-preserving tie-break.tie_break="random": seeded random tie-break usingrandom_state.
Stable tie-breaking is reproducible but can favor earlier feature/alliance
order in exact ties. Increase allocation_seats to reduce the practical impact
of integer ties.
Outputs
explanation.to_feature_frame() returns local feature attributions:
featureattributionabs_attributionsource_allianceeffectdirectionrelative_sharesign_consistentis_residual- residual rows such as
__excluded__or__below_threshold__when relevant
explanation.to_alliance_frame() returns alliance-level votes and seats:
votespositive_votesnegative_votespositive_seatsnegative_seatssource_attributionrepresented_attributionsource_raw_attributionrepresented_raw_attribution- threshold status
Diagnostic fields on the explanation object include:
raw_attribution_sumprojection_targetprojection_residualprojection_residual_ratioexcluded_residualbelow_threshold_residualresolved_output_typeperturbationaffinity_modetie_break
Global Explanation
explanations = explainer.explain_many(
X_test.head(50),
random_state=42,
reuse_background_sample=False,
n_background=50,
)
global_frame = explainer.explain_global(
X_test,
max_rows=50,
random_state=42,
reuse_background_sample=False,
seats=100,
alliance_mode="none",
n_background=50,
)
print(global_frame)
print(explainer.global_alliance_matrix_)
explainer.plot_global_importance(global_frame)
explainer.plot_global_alliance_heatmap()
The global output includes:
global_abs: mean absolute DhondtXAI attributiondirectional: mean signed attributionpositive: mean positive attributionnegative: mean negative attributionthreshold_survival: frequency of threshold eligibilityis_residual: marks__excluded__and__below_threshold__rows
By default, global explanations use controlled but different background samples
for each row. Set reuse_background_sample=True when you want the same random
background sample reused across all rows.
Diagnostics And Reports
print(explanation.summary(top_k=8))
print(explanation.summary(top_k=8, language="tr"))
print(explanation.diagnostics())
projection_residual_ratio should be monitored. A low value means the raw
D'Hondt representation naturally matches the local model difference; a high
value means the conservative projection made a larger correction and the
explanation should be interpreted with more caution.
Interpretation guide:
0.00 - 0.10: low correction; explanation is closer to raw D'Hondt evidence.0.10 - 0.50: medium correction; interpret with caution.> 0.50: high correction; raw D'Hondt evidence required a large projection.
The text report prints an explicit warning when this correction is medium or high.
Plots
plot_local_bar(...): signed local feature bar plot.plot_waterfall(...): baseline-to-score additive waterfall.plot_signed_parliament(...): positive/negative evidence parliament.plot_global_importance(...): residual-aware global importance.plot_global_alliance_heatmap(...): global alliance co-occurrence matrix.
Limitations
- DhondtXAI is a beta tabular XAI library, not a mature SHAP/LIME replacement.
- Current removal sampling is background-interventional, not fully conditional.
conditional_knnis an approximate local sampler, not a causal conditional distribution estimator.user_samplercan improve domain realism, but its validity depends on the sampler supplied by the user.- Low
allocation_seatsvalues can produce sparse or order-sensitive attributions; the default uses high-resolution allocation. - Stable D'Hondt tie-breaking is order-preserving and should be documented when exact ties matter.
- Background replacement can create out-of-distribution rows for strongly correlated features.
- Auto-alliance and interaction estimation can be expensive for high-dimensional data.
- Projection residuals should be monitored and reported.
- Probability-scale explanations may be less additive than logit-scale explanations.
- Below-threshold and excluded residuals should be interpreted explicitly.
Building A PyPI-Ready Package
The package metadata includes README long description, version, license, citation, and optional sklearn/dev extras. To build local distribution files:
python -m pip install --upgrade build twine
python -m build
python -m twine check dist/*
Then a built wheel can be installed locally with:
pip install dist/dhondtxai-0.9.1-py3-none-any.whl
Publishing to PyPI requires a PyPI API token:
python -m twine upload dist/*
Legacy API
The previous global feature-importance workflow is still available:
features, votes, excluded = explainer.apply_dhondt(
num_votes=100000000,
num_mps=600,
threshold=5,
)
seats = explainer.dhondt_method(votes, 600, excluded)
explainer.plot_results(features, seats)
This legacy path uses model.feature_importances_. The new proposed method is
explain(...).
Citation
T. B. Donmez, "Explainable AI through a Democratic Lens: DhondtXAI for Proportional Feature Importance Using the D'Hondt Method," 2024. https://doi.org/10.48550/arXiv.2411.05196
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 dhondtxai-0.9.1.tar.gz.
File metadata
- Download URL: dhondtxai-0.9.1.tar.gz
- Upload date:
- Size: 395.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cec6f555ea003c4d60629381f0f6432d01af3509ee2b5ad7c7f7cba6ca0adb6e
|
|
| MD5 |
41742df73c8d4eb1c512771a9345298c
|
|
| BLAKE2b-256 |
cb1e1f2c2e95edb857eb59ced2c1d4a28101460e95f46639a0ce155ff387975a
|
File details
Details for the file dhondtxai-0.9.1-py3-none-any.whl.
File metadata
- Download URL: dhondtxai-0.9.1-py3-none-any.whl
- Upload date:
- Size: 27.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3f22efa099adca738e3904411d95f3b1ae89377be48c85cc319838d8dd91f5b1
|
|
| MD5 |
7759f72560cc7c8bbfbb40152fc79bb3
|
|
| BLAKE2b-256 |
c06f96729f474e00ae902d0936736af49960655b9a5e3cfb827cd85bf264c93f
|