Metadata-Version: 2.4
Name: croma
Version: 0.1.0
Summary: Robustness metrics for pathology foundation models: RI, MaRI, CRoMa, and APD/nAPD.
Project-URL: Homepage, https://github.com/clemsgrs/croma
Project-URL: Documentation, https://clemsgrs.github.io/croma/
Project-URL: Repository, https://github.com/clemsgrs/croma
Project-URL: Issues, https://github.com/clemsgrs/croma/issues
Project-URL: Changelog, https://github.com/clemsgrs/croma/blob/main/CHANGELOG.md
Author: Jeroen van der Laak, Geert Litjens
Author-email: Clément Grisi <clement.grisi@radboudumc.nl>
License-Expression: Apache-2.0
License-File: LICENSE
License-File: NOTICE
Keywords: foundation-models,metrics,pathology,representation-learning,robustness
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
Requires-Python: >=3.10
Requires-Dist: numpy>=1.24
Requires-Dist: pandas>=2.0
Requires-Dist: scikit-learn>=1.3
Requires-Dist: tqdm>=4.66
Provides-Extra: dev
Requires-Dist: black~=26.5; extra == 'dev'
Requires-Dist: matplotlib>=3.8; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: scipy>=1.11; extra == 'dev'
Provides-Extra: docs
Requires-Dist: furo; extra == 'docs'
Requires-Dist: sphinx-autodoc-typehints; extra == 'docs'
Requires-Dist: sphinx-copybutton; extra == 'docs'
Requires-Dist: sphinx>=8.1; extra == 'docs'
Provides-Extra: repro
Requires-Dist: huggingface-hub>=0.24; extra == 'repro'
Requires-Dist: matplotlib>=3.8; extra == 'repro'
Requires-Dist: pillow>=10.0; extra == 'repro'
Requires-Dist: pyarrow>=14.0; extra == 'repro'
Requires-Dist: pymupdf>=1.24; extra == 'repro'
Requires-Dist: rich>=13.0; extra == 'repro'
Requires-Dist: scipy>=1.11; extra == 'repro'
Requires-Dist: timm; extra == 'repro'
Requires-Dist: torch; extra == 'repro'
Requires-Dist: transformers; extra == 'repro'
Description-Content-Type: text/markdown

# croma

<p align="center">
  <a href="https://pypi.org/project/croma/"><img src="https://img.shields.io/pypi/v/croma.svg" alt="PyPI version"></a>
  <a href="https://clemsgrs.github.io/croma/"><img src="https://img.shields.io/badge/docs-github.io-blue.svg" alt="Documentation"></a>
  <a href="https://github.com/psf/black"><img src="https://img.shields.io/badge/code%20style-black-000000.svg" alt="Code style: Black"></a>
  <a href="LICENSE"><img src="https://img.shields.io/github/license/clemsgrs/croma.svg" alt="License"></a>
</p>

`croma` measures how much a pathology foundation model's representation is driven by biology rather than by non-biological technical variation -- staining, scanning, tissue preparation -- across centers.

| Metric | Name | What it does |
| --- | --- | --- |
| `RI` | Robustness Index | Counts favourable vs. unfavourable neighbours |
| `MaRI` | Margin-aware Robustness Index | Weights that same evidence by feature distance |
| `CRoMa` | Cross-confounder Robustness Margin | A signed margin, with tail-aware reporting |

RI was introduced in the [PathoROB](https://arxiv.org/abs/2507.17845) study. `croma` provides a clean re-implementation of it, adds MaRI as its margin-aware extension, and introduces CRoMa, which overcomes limitations of both.

📖 **[Documentation](https://clemsgrs.github.io/croma/)**

## Install

```bash
pip install croma
```

The core package depends only on `numpy`, `pandas`, `scikit-learn` and `tqdm`. It never loads a model or reads an image -- you bring the embeddings. Add the paper-reproduction utilities with `pip install "croma[repro]"`.

## Quickstart

You need a **manifest** CSV with one row per sample, and an **embeddings** array of shape `(N, D)` whose row `i` is the embedding of manifest row `i`. Don't normalize them -- `croma` L2-normalizes internally and compares neighbours by cosine distance.

```python
import numpy as np
import pandas as pd
from croma import CRoMa, MaRI, RI

manifest = pd.read_csv("manifest.csv")
features = np.load("embeddings.npy")

common = dict(confounder_column="center", evaluation_design="paired_2x2")

ri = RI.compute(features, manifest, k_candidates=[5, 11, 21], **common)
mari = MaRI.compute(features, manifest, k_candidates=[5, 11, 21], **common)
croma = CRoMa.compute(features, manifest, **common)

print(f"RI    {ri.value:.3f}  (k={ri.k}, undefined {ri.undefined_frac:.1%})")
print(f"MaRI  {mari.value:.3f}  (tau={mari.tau:.4f})")
print(f"CRoMa {croma.value:+.3f}  (lower-tail mean {croma.ltm_alpha:+.3f})")
```

## Reading the numbers

**RI** and **MaRI** live in `[0, 1]`; above `0.5`, biological evidence outweighs confounder evidence. **CRoMa** lives in `(-1, 1)` and is neutral at `0`, positive when biology dominates.

Three habits will keep you out of trouble:

1. **Always read `undefined_frac` next to RI and MaRI.** Samples with no informative neighbour in their top `k` are excluded from the score, so a high RI over a thin support is not a strong result.
2. **Never pin `tau`.** It defaults to `None`, which resolves it per model on the scale of that model's own neighbour distances. One fixed `tau` shared across models sharpens the margin for some and flattens it for others -- exactly the distortion MaRI exists to remove. See [Choosing tau](https://clemsgrs.github.io/croma/metrics.html#choosing-tau).
3. **Read the tail, not just the mean.** `croma.ltm_alpha` is the mean of the worst 10% of samples. Pooled scores hide brittle subgroups.

## Also in the docs

- **[Evaluation designs](https://clemsgrs.github.io/croma/manifest.html)** -- `paired_2x2` controls what is compared and reports occurrence-level outputs; `dataset_wide` gives one number over the whole cohort at sample level. Includes the manifest contract and minimal valid examples for each.
- **[CLI](https://clemsgrs.github.io/croma/cli.html)** -- the same three metrics from the shell, over a `.npy` that already exists.
- **[Benchmarking](https://clemsgrs.github.io/croma/benchmarking.html)** -- the multi-model pipeline that produced the paper's numbers, split into embed / compute / render steps under `scripts/`.

## Citing

The paper describing MaRI and CRoMa is in preparation. Until it is out, please cite this
repository — use the **Cite this repository** button, or [`CITATION.cff`](CITATION.cff)
directly — along with the [PathoROB](https://arxiv.org/abs/2507.17845) study that
introduced the Robustness Index.

## License

[Apache 2.0](LICENSE)
