Metadata-Version: 2.4
Name: robustpixelmaker
Version: 0.2.0
Summary: Reproducible, leakage-safe pixel/patch feature selection for registered scientific images: the image sibling of RobustModelMaker
Author-email: "Amanda S. Barnard" <amanda.s.barnard@anu.edu.au>
License: MIT
Project-URL: Homepage, https://github.com/amaxiom/RobustPixelMaker
Project-URL: Changelog, https://github.com/amaxiom/RobustPixelMaker/blob/main/CHANGELOG.md
Project-URL: Documentation, https://github.com/amaxiom/RobustPixelMaker/blob/main/README.md
Project-URL: Bug Tracker, https://github.com/amaxiom/RobustPixelMaker/issues
Project-URL: Repository, https://github.com/amaxiom/RobustPixelMaker
Project-URL: RobustMaker (tabular), https://github.com/amaxiom/RobustModelMaker
Project-URL: RobustMaker (signals), https://github.com/amaxiom/RobustSignalMaker
Project-URL: Related (benchmarking), https://github.com/amaxiom/benchmake
Keywords: feature selection,stability selection,scientific imaging,reproducibility,nested cross-validation,image analysis
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Scientific/Engineering :: Image Processing
Classifier: Topic :: Scientific/Engineering :: Information Analysis
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.22
Requires-Dist: scipy>=1.8
Requires-Dist: scikit-learn>=1.1
Provides-Extra: parallel
Requires-Dist: joblib>=1.1; extra == "parallel"
Provides-Extra: plots
Requires-Dist: matplotlib>=3.5; extra == "plots"
Provides-Extra: all
Requires-Dist: joblib>=1.1; extra == "all"
Requires-Dist: matplotlib>=3.5; extra == "all"
Dynamic: license-file

# RobustPixelMaker

**Reproducible, leakage-safe feature selection that works directly on image pixels.**

RobustPixelMaker (RPM) finds the informative region of a set of registered scientific
images, working on the pixels themselves with no collapse to engineered features, and
returns a robust predictor that reads only that region, a reproducible selected region,
and an honest performance estimate.

## Install

```bash
pip install robustpixelmaker
```

Python 3.9 or newer. Three required dependencies, all of which you almost certainly
already have: `numpy>=1.22`, `scipy>=1.8`, `scikit-learn>=1.1`. There is no
deep-learning dependency; the gated mask is numpy with hand-derived gradients.

Two optional extras: `[parallel]` adds joblib for parallel resampling, `[plots]` adds
matplotlib for the figures, and `[all]` takes both. The package ships a `py.typed`
marker, so its annotations are used by your type checker.

## The problem it solves

Scientific images are mostly not signal. A micrograph is largely substrate, an MRI
largely skull and background, a microscopy field largely plasma. Training on all of it
spends capacity on noise and invites the model to learn the background instead of the
thing you care about.

The usual answers are unsatisfying. Ranking pixels by variance keeps whatever fluctuates
most, which is not the same as what predicts. Post-hoc saliency explains one fitted model
and moves when you retrain it. Collapsing to radiomics or ROI features pre-commits you to
a recipe, and the selection then describes the recipe as much as the data.

RPM instead asks which pixels are *predictively sufficient and reproducibly so*, and
answers with a region plus a number saying how much to trust it.

## Quick start

```python
from robustpixelmaker import RobustPixelMaker

# X: (n_images, height, width), registered into a common frame. y: labels or values.
rpm = RobustPixelMaker(registered=True, random_state=0).fit(X, y)

rpm.summary()             # leakage-safe nested-CV score plus selection diagnostics
rpm.pi_map()              # selection-frequency heatmap, in image space
rpm.stability_report()    # does a reproducible region exist at this sample size?
rpm.coverage()            # fraction of the image retained
rpm.predict(X_new)        # the robust predictor, reading only the selected region
rpm.save("run_output/")   # JSON, CSV and npy artefacts
```

The model fitted on the selected region is a separate choice from the selection itself:

```python
RobustPixelMaker(model="rf")        # mask, logreg, ridge, gb, svm, knn, or an estimator
```

`None` (the default) keeps the mask's own head. The choice never changes which patches
are selected, only what is fitted on them.

Tasks (binary, multiclass, regression) are inferred from `y`. The one argument worth
owning explicitly is `registered`: a single shared mask only means something when the
images sit in a common coordinate frame. `registered=False`, for per-image conditional
masks over unregistered fields of view, is a documented extension point and raises
rather than guessing.

## What you get back

- **A selected region**, indexed by pixel, never by a derived feature space.
- **A selection-frequency map** (`pi_map()`), the fraction of bootstrap resamples in
  which each patch survived. Its *shape* is the diagnostic: bimodal means a reproducible
  region exists, diffuse means the data cannot support one at this sample size.
- **An honest score**, from nested cross-validation in which the selection itself is
  refitted inside every fold, so nothing about the held-out data touches the selection.
- **A robust predictor** trained only on the retained region.
- **A verdict** against the full-image baseline: preserved, significantly better, or
  significantly worse.

## How it works

1. **A learnable patch mask.** Hard-Concrete L0 gates over a patch grid, trained in two
   phases: fit the head on all patches, freeze it, then prune the mask. Freezing matters,
   because trained jointly the head simply grows to cancel a shrinking mask.
2. **Bootstrap stability selection.** Meinshausen-Buehlmann frequencies over resamples
   (bootstrap, half-subsample, or Shah-Samworth complementary pairs), so a patch that
   only survives on one particular sample drops out.
3. **Marginalisation over representations.** The distinguishing step: the selection is
   integrated over an ensemble of lenses (patch means, texture statistics, coarser
   scales, random convolutional features) as well as over data resamples. The lens is
   treated as a nuisance variable rather than a choice to defend. On the shipped control,
   single-lens precision ranges from 0.36 to 1.00 depending on which lens you happen to
   pick; marginalising reaches 1.00 without knowing in advance which was right.
4. **Absent, not zero.** Dropped regions are handled by renormalised (partial)
   convolution, so a mask boundary does not fabricate an edge.

Everything is numpy and scipy with hand-derived analytic gradients. There is no torch
dependency; GPU masking is an optional future backend behind the same interfaces.

## Honest about its limits

The library ships its negative results rather than hiding them, because a selection
method that cannot say "no region here" is not trustworthy:

- On a well-powered compact task the frequency map is sharply bimodal (ambiguous
  fraction 0.14). On an n=780 ultrasound set it is diffuse (0.57), and no amount of extra
  resampling manufactures a region. Bootstrap aggregation is a stability **estimator**,
  not a stability generator.
- Raw selection-set Jaccard is confounded by coverage, so a chance-corrected variant
  (`adjusted_jaccard`) is provided and preferred when comparing operating points.
- Holding the stability wrapper fixed and swapping only the base ranker, the learned
  mask adds nothing over random-forest importance on real data (p = 1.000) and is the
  less stable of the two. Earlier comparisons confounded the mask with the wrapper.
- The shipped synthetic control has a linear label rule and the mask's head is linear by
  default, so the benchmark where the method wins largest is one where its inductive
  bias is correct by construction. On purely interactive labels the linear head recovers
  nothing at all; `head="mlp"` repairs part of that.
- All of this is recorded, with the statistics and the corrections, in the repository's
  benchmark findings rather than summarised away.

## The RobustMaker family

| Package | Selects | Data |
|---|---|---|
| RobustModelMaker | columns | tabular features |
| RobustPixelMaker | patches | scientific images |
| RobustSignalMaker | points and bands | signals and spectra |

All three do leakage-free bootstrap stability selection, return a reproducible selected
set with an explicit stability number, and report an honest nested-CV score.

## Documentation and source

The literature positioning, six worked scientific examples, the complete benchmark
suite and the findings behind every claim above, including the ones that went against
the method, are in the repository:

**https://github.com/amaxiom/RobustPixelMaker**

## Citation

If RobustPixelMaker contributes to published work, please cite the repository and the
lineage it builds on:

- S. Li and A. S. Barnard, *Variance tolerance factors for interpreting all neural
  networks*, IJCNN 2023.
- A. S. Barnard, *RobustModelMaker*, arXiv:2606.01566.

## Licence

MIT. Copyright 2026 Amanda S. Barnard.
