Skip to content

Repository files navigation

Poniard

Poniard logo

A poniard /ˈpɒnjərd/ or poignard (Fr.) is a long, lightweight thrusting knife (Wikipedia).

Poniard is a scikit-learn companion for multi-model diagnostics. Fit a handful of models, cross-validate them side by side, then answer the questions that actually matter:

  • Where do the models fail, and why?
  • Is model A really better than B, or is that fold noise?
  • Which model is good enough and cheap enough?

Then export a plain sklearn pipeline you own and leave. Not AutoML. Not end-to-end. Every feature earns its place.

Installation

pip install poniard

Plotting is an optional extra:

pip install poniard[plot]

Quick start

from sklearn.datasets import make_classification
from poniard import PoniardClassifier

X, y = make_classification(n_samples=200, n_features=10, random_state=42)

clf = PoniardClassifier()
clf.setup(X, y)    # configure: infer types, build preprocessing, CV, pipelines
clf.fit(X, y)      # cross-validate every estimator
clf.get_results()  # leaderboard with a dummy baseline

setup comes first on purpose: it lets you see what fit will do to your data and modify it before anything is cross-validated. fit(X, y) will also configure on its own if you skip it, but the preprocessor is never a black box — inspect clf.feature_types, reassign types, or add preprocessing steps between the two calls (see the in-depth guide).

The core loop

Poniard is built around one loop: compare → explain → decide → export.

1. Compare

clf = PoniardClassifier()                 # or PoniardRegressor()
clf.setup(X, y)                           # configure, then adjust preprocessing if needed
clf.fit(X, y)
clf.get_results()                         # mean scores, fit/score times

Every estimator is cross-validated on the same folds, with a DummyClassifier / DummyRegressor baseline included automatically.

2. Explain — error analysis

ErrorAnalyzer answers where and why your models fail. One call returns a structured ErrorReport:

from poniard.error_analysis import ErrorAnalyzer

report = ErrorAnalyzer.from_poniard(clf).analyze(X, y)

report.universal_failures   # samples every model got wrong
report.disagreement_set     # samples where models split (ensembling candidates)
report.lift_by_target       # classes/bins over-represented in errors (lift > 1)
report.lift_by_feature      # feature values over-represented in errors

3. Decide

clf.compare()                    # paired fold tests: is A really better than B?
clf.pareto()                     # best metric vs training time (Pareto front)
clf.best_under(seconds=0.5)      # best model within a time budget

4. Export

model = clf.get_estimator("LogisticRegression", retrain=True, X=X, y=y)
# a fitted sklearn.pipeline.Pipeline with no poniard references — deploy it

Feature overview

Area What you get
Error analysis Universal failures, disagreement sets, lift vs baseline — ranked per sample, sliced by target and features
Statistical comparison Paired fold tests so you stop trusting fold-mean leaderboards
Time / quality Pareto front and best-under-budget helpers
Preprocessing Automatic numeric / categorical / datetime type inference, imputation, encoding, scaling; per-estimator preprocessors (preprocessor_map) — HistGradientBoosting automatically uses a native NaN/categorical profile
Tuning Grid, random, and halving search that re-enters the experiment as a new named estimator
Ensembles Diversity-aware voting / stacking built from your fitted estimators
Plotting Metrics, ROC, confusion matrices, residuals, feature importance (optional, requires [plot])

Docs and examples

  • In-depth guide — the full workflow, preprocessing internals, error-analysis semantics, tuning, ensembles, plotting, and export.
  • Examples — runnable scripts, executed in CI so they never go stale:
    • examples/00_getting_started.py — fit, results, predictions
    • examples/01_error_analysis.py — full failure-forensics workflow
    • examples/02_plotting.py — the plotting API
    • examples/03_preprocessing.py — per-estimator preprocessors and the native profile
python examples/00_getting_started.py

Estimator naming

Estimators are named by class name, with collision suffixes (_2, _3, ...). Override names with tuple syntax:

clf = PoniardClassifier(estimators=[("my_lr", LogisticRegression())])
# pipelines: {'my_lr': ..., 'DummyClassifier': ...}

Environment variables

  • PONIARD_TQDM_LEAVE — set to "True" to keep progress bars on screen after fitting completes. Default "False".

Python support

3.10–3.13, tested on Linux, macOS, and Windows.

Development

git clone https://github.com/rxavier/poniard.git
cd poniard
uv sync --dev
uv run pytest

License

MIT

About

Streamline dataset debugging and model benchmarking with scikit-learn.

Topics

Resources

Stars

143 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages