Feature Prototype Direction Explainer (FPDE) is a Python package for prototype-contrast feature attribution. It explains a classification result by comparing an input with a prototype for the target class and a prototype for a rival class, then decomposing that contrast into per-feature contributions.
Use FPDE when you want a lightweight, post-hoc explanation method for tabular feature vectors and black-box classifiers that expose class probabilities.
- Build class-mean prototypes from training data.
- Explain one sample with Diff-FPDE, Cos-FPDE, or a fixed Hyb-FPDE mixture.
- Explain batches while reusing fitted prototype state.
- Search Diff, Cos, and Hyb-FPDE candidate settings.
- Select
lambda_hybwith held-out deletion and insertion validation. - Build an experimental Bayesian posterior over Hyb-FPDE
lambda_hybcandidates. - Compute deletion and insertion perturbation curves for an attribution vector.
- Plot attribution bars, cumulative waterfalls and contribution summaries, attribution heatmaps, FPDE-native prototype similarity distributions, and perturbation curves.
FPDE requires Python 3.12 or newer.
python -m pip install fpdePlotting helpers use matplotlib as an optional dependency:
python -m pip install "fpde[plot]"For local development, clone the repository and install it in editable mode:
python -m pip install -e .The PyPI distribution name and Python import package are both fpde.
This example trains a scikit-learn classifier, fits an FPDE engine on the training data, and explains one test sample.
import numpy as np
from sklearn.datasets import load_breast_cancer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from fpde import FPDEEngine
data = load_breast_cancer()
X_train, X_test, y_train, _ = train_test_split(
data.data,
data.target,
test_size=0.25,
random_state=7,
stratify=data.target,
)
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
model = LogisticRegression(max_iter=2000, random_state=7)
model.fit(X_train, y_train)
engine = FPDEEngine.fit(X_train, y_train, model=model)
attributions, details = engine.explain_one(X_test[0], lambda_hyb=0.5)
print(np.asarray(attributions))
print(details["target_label"], details["rival_label"], details["evidence"])Positive attribution values support the target class relative to the rival class. Negative values support the rival class relative to the target class.
To visualize an explanation, install the optional plotting extra and pass feature names when available:
from fpde import plot_attribution_waterfall, plot_attributions
plot_attributions(
attributions,
feature_names=data.feature_names,
top_k=10,
title="Top FPDE feature contributions",
)
plot_attribution_waterfall(
attributions,
feature_names=data.feature_names,
title="Cumulative FPDE evidence",
)For Bayesian-FPDE plots, plot_attributions also accepts interval_low and
interval_high so you can show the attribution range induced by the
lambda_hyb credible interval.
For a local contribution view, use plot_local_contributions. This displays FPDE local
attributions as a signed feature bar chart:
from fpde import plot_local_contributions
ax = plot_local_contributions(
data.feature_names,
attributions,
values=X_test[0],
top_k=10,
title="FPDE local explanation",
)
ax.figure.savefig("fpde_local_contributions.png", dpi=160, bbox_inches="tight")For a compact plotting namespace for FPDE contributions, use fpde.plots.
These helpers visualize FPDE attribution arrays directly:
from fpde.plots import FPDEPlotExplanation, bar, beeswarm, scatter, waterfall
batch_attributions, _ = engine.explain_batch(X_test[:20], lambda_hyb=0.5)
plot_exp = FPDEPlotExplanation(
values=batch_attributions,
data=X_test[:20],
feature_names=data.feature_names,
)
ax = beeswarm(plot_exp, show=False, title="FPDE contribution summary")
ax.figure.savefig("fpde_beeswarm.png", dpi=150, bbox_inches="tight")
bar(plot_exp, show=False)
scatter(plot_exp, feature=data.feature_names[0], show=False)
waterfall(
values=attributions,
base_value=0.0,
prediction=float(np.sum(attributions)),
feature_names=data.feature_names,
show=False,
)You can also compare how training samples and the explained sample relate to target and rival prototypes:
from fpde import plot_prototype_similarity_distribution
target_idx = np.where(engine.prototype_labels == details["target_label"])[0][0]
rival_idx = np.where(engine.prototype_labels == details["rival_label"])[0][0]
plot_prototype_similarity_distribution(
X_train,
engine.prototypes[target_idx],
rival_prototype=engine.prototypes[rival_idx],
x=X_test[0],
metric="cosine",
title="FPDE prototype similarity distribution",
)To select a Hyb-FPDE mixture weight with Bayesian-FPDE, use held-out samples to build a posterior over lambda candidates, then explain with the posterior mean:
selection = engine.select_bayesian_lambda(
X_test[:16],
lambda_hyb_grid=(0.0, 0.25, 0.5, 0.75, 1.0),
)
attributions, details = engine.explain_one_bayesian(X_test[0], selection)
print(selection.posterior_mean_lambda, selection.map_lambda)
print(selection.credible_interval)In v0.1.0, Bayesian-FPDE uncertainty is limited to the finite grid of
lambda_hyb candidates. It does not sample class prototypes, estimate
feature-level prototype uncertainty, or model black-box classifier uncertainty.
python examples/minimal_fpde_example.pyThe script prints the predicted class and the largest positive and negative feature contributions for one sample from the breast cancer dataset bundled with scikit-learn.
For the experimental Bayesian-FPDE lambda posterior workflow, see
examples/bayesian_fpde_example.ipynb.
- Method overview: core FPDE concepts and variants.
- API reference: public functions, classes, parameters, and result objects.
- Reproducibility checklist: what to record when reporting FPDE experiments.
- Data and code availability: repository and dataset availability statement.
- Repository metadata: project URL, description, topics, and important paths.
- Release notes: package history.
Install the development dependencies, then run the test suite:
python -m pip install -e .
python -m pytestYou can also run the repository in Docker:
docker build -t fpde .
docker run --rm fpdeThe Docker command runs the test suite and the minimal example.
If you use FPDE in academic work, cite the software or method as appropriate. Citation metadata is available in CITATION.cff.
FPDE is distributed under a dual license: MIT OR Apache-2.0. You may choose either license. See LICENSE, LICENSE-MIT, and LICENSE-APACHE.