CASPER-t
Covering Approximation Space Permutation Elimination Rule (t-statistic form) a reliability-weighted topological feature selector, packaged as a drop-in scikit-learn transformer.
Keep a small, confident subset of features instead of all of them without paying for it in accuracy. On 9 benchmark datasets CASPER-t retains a median 60 % of features while tracking full-feature accuracy to within ~1–2 points.
pip install casper-fs
How it works
CASPER-t treats each feature's usefulness as a measured quantity with an error bar, and keeps a feature only when its measured contribution clears a reliability margin not merely when it looks large once.
flowchart LR
A["X, y"] --> B["Jenks<br/>discretization"]
B --> C["L₁ tolerance<br/>relation"]
C --> D["select ε*<br/>(covering radius)"]
D --> E["γ-PFI<br/>N permutations"]
E --> F["tᶠ = (Δγ − Q1) / SEM > κ"]
F --> G["selected<br/>subset"]
-
Discretize each feature by unsupervised Jenks natural breaks.
-
Build an L₁ tolerance relation and pick the covering radius ε* that maximises the γ dependency degree (just below saturation).
-
Score every feature by the drop in γ under N permutations (γ-PFI). A permutation-importance signal native to the covering approximation space.
-
Retain a feature only when that drop clears the first quartile of the feature-wise distribution by a reliable margin:
tᶠ = (mean(Δγ_f) − Q1) / SEM_f > κ (default κ = 1.5)
Everything is refit on the training split it is given, so it is leakage-free
inside a scikit-learn Pipeline / cross-validation loop.
Benchmark
Protocol. Nested cross-validation on 9 datasets, evaluated across 8 classifiers, against 13 feature-selection baselines (filter, wrapper, embedded, and rough-set families). Baselines are compared at matched cardinality. Each is truncated to CASPER-t's own per-fold feature count. So, every method is judged on the same feature budget.
The headline is parsimony. CASPER-t discards 25–51 % of the features yet stays within a point or two of using everything and matches or beats the full set on 4 of 9 datasets (banknote, diabet, htru2, sonar).
| Dataset | Features kept (K / p) | CASPER-t acc. | All-features acc. | Δ |
|---|---|---|---|---|
| banknote | 3.0 / 4 | 96.8 | 97.1 | −0.2 |
| htru2 | 5.7 / 8 | 97.1 | 97.0 | +0.1 |
| magic | 7.0 / 10 | 81.2 | 82.5 | −1.3 |
| diabet | 6.7 / 10 | 77.6 | 76.5 | +1.1 |
| phoneme | 3.0 / 5 | 77.7 | 81.9 | −4.2 |
| cyron | 3.6 / 6 | 84.9 | 87.7 | −2.9 |
| sonar | 34.0 / 60 | 80.3 | 80.2 | +0.2 |
| parkinson | 8.4 / 17 | 86.2 | 86.4 | −0.3 |
| kanser | 12.7 / 26 | 94.2 | 96.0 | −1.8 |
Accuracy is the mean over 8 classifiers × outer folds. K is the mean per-fold subset size, p is the number of candidate features.
On honest terms. At a matched feature budget no single selector dominates the
field. CASPER-t's mean Accuracy rank is mid-pack (≈ 7.7 of 14 methods), and
its value is the combination of a principled topological criterion with a
reliability gate that yields compact subsets. Every rank, dispersion (fold std and
across-classifier std), and per-fold number behind these figures is shipped in
detailed_tables/, nothing is averaged away.
Quickstart
from casper import CasperT
from sklearn.pipeline import Pipeline
from sklearn.svm import SVC
from sklearn.model_selection import cross_val_score
from sklearn.datasets import load_breast_cancer
X, y = load_breast_cancer(return_X_y=True)
# standalone
sel = CasperT(kappa=1.5, n_permutations=30, random_state=42).fit(X, y)
print(sel.get_support().sum(), "features selected") # -> 13 of 30
print(sel.eps_star_, sel.t_stats_) # diagnostics
# leakage-free inside a Pipeline (refits per fold)
pipe = Pipeline([("casper", CasperT()), ("clf", SVC())])
cross_val_score(pipe, X, y, cv=5)
Requires Python ≥ 3.9, numpy, scipy, scikit-learn. Install from a clone with
pip install ..
API
CasperT(kappa=1.5, n_permutations=30, max_bins=7, target_gvf=0.80, max_topological=3000, random_state=42)
| Parameter | Meaning |
|---|---|
kappa |
reliability margin of the tᶠ rule; larger ⇒ fewer, more confident features |
n_permutations |
permutation replicates per feature in γ-PFI |
max_bins / target_gvf |
Jenks bin cap / goodness-of-variance-fit early-stop target |
max_topological |
subsample cap for the O(n²) topological stage (evaluation uses full data) |
Fitted attributes: support_, delta_gamma_, sem_, t_stats_, eps_star_,
gamma_base_, q1_. Standard SelectorMixin API: fit, transform,
get_support, get_feature_names_out.
Reproducing the article
pipeline/gamma_pfi_uhem.py is the full nested-CV pipeline used for the paper
(all baselines, statistics, and per-fold outputs); pipeline/make_tables.py
regenerates the LaTeX tables from those outputs.
Detailed results (detailed_tables/)
The article's tables report means only, for readability. These files hold the same numbers with their dispersion and at fold-level granularity:
| File | What it is |
|---|---|
summary_mean_std.csv |
one row per (dataset × method × metric): mean, fold std, across-classifier std, retained K, p, retained % |
fold_level_hybrid.csv |
every outer fold, HYBRID classifier only |
fold_level_raw.csv.gz |
every outer fold × 8 classifiers × every method |
Method ids: V1_tstat = CASPER-t; *_matchK = baselines truncated at CASPER-t's
per-fold cardinality; *_native = rough-set baselines at their own stopping point;
ALL = full feature set.
Changelog
0.1.3 — Fixed a bug in tstat_support (the t_f reliability rule): a
zero-variance or near-zero-variance feature (SEM_f ≈ 0) could get a
spurious, arbitrarily large t-statistic from the + 1e-9 denominator guard
whenever Q1 < 0, causing it to be auto-selected regardless of kappa even
though it carries no measurable signal. tstat_support now falls back to
the point estimate (Δγ_f) when SEM_f is ~0, rather than dividing by it.
If you're on <=0.1.2 and your feature set can contain constant or
extremely low-variance columns (e.g. one-hot/multi-hot encodings with rare
categories), upgrade and re-run — your selected-feature set may change.
Citation
If you use CASPER-t, please cite the article:
@article{casper_t,
title = {A Topological Feature Selection Framework via Generalized Covering
Approximation Spaces with Permutation Importance},
authors = {Uğur Yiğit, Kenan Evren Boyabatlı, Ebrar Karakurt, Esma Nur Elgün and
Ertan Sönmez},
journal = {Preprint - Appearing}
year = {2026}
}
License
MIT — see LICENSE.
Release files for casper-fs 0.1.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| casper_fs-0.1.3.tar.gz | 333.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| casper_fs-0.1.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 345.5 kB
Release files / casper_fs-0.1.3.tar.gz
| Download URL | casper_fs-0.1.3.tar.gz |
|---|---|
| Size | 333.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c296699f50358ed0b7e81b30d58cde0b210be06e307042e9c8fbeac009e253ac
|
|
BLAKE2b-256 checksum How to use checksums |
11e4df299a117cabbf5f524857a289b400d66c0aa0a61819dc74af29963bca38
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|
Release files / casper_fs-0.1.3-py3-none-any.whl
| Download URL | casper_fs-0.1.3-py3-none-any.whl |
|---|---|
| Size | 11.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
35fcd73550c29479f76237358b866c14ea9217e93dd02cb113dd3e0d31a91658
|
|
BLAKE2b-256 checksum How to use checksums |
0c23ad07f83e988b1dfd542940fe6b1143195661eb1bd18085b72f2c7458e89e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|