Manifest¶
Every metric takes the same two inputs: a manifest describing the samples, and an embedding matrix holding one feature vector per manifest row.
Manifest contract¶
Column |
Required |
Notes |
|---|---|---|
|
yes |
Unique identifier for the sample. |
|
yes |
Path to the source image. Used for provenance and deduplication, never read by the metrics themselves. |
|
yes |
The biological class. This is the signal a robust model should organize by. |
|
yes |
The sample’s independence group: the non-independent source it came from – a
slide, a patient, a specimen, an acquisition, whatever unit your study declares.
Candidates sharing a query’s |
confounder |
yes |
The non-biological factor to test against – center, scanner, stain protocol. You
choose the column name and pass it as |
|
only for |
Which 2x2 evaluation subset the row belongs to. See below. |
|
no |
Free-text name echoed back on the result. Defaults to |
Embedding contract¶
Property |
Requirement |
|---|---|
Type |
|
Shape |
Exactly 2-D, |
|
Must equal |
|
Any positive width. Models with different |
dtype |
Any floating dtype. |
Normalization |
Do not normalize. |
Missing values |
Not supported, and not silently tolerated: a |
Row order is the whole contract. If you produced embeddings by deduplicating repeated images, expand them back to manifest-row order first – see Deduplicated embeddings.
Evaluation designs¶
all is the default everywhere in the library: every supplied manifest row is
scored together, as one evaluation scope, for a single number over the whole cohort. It
accepts that label and confounder may be unevenly mixed, needs no subset column, and is
the right first thing to run on a new dataset.
Ask for paired_2x2 when you want to control what is being compared: you hand-build
subsets where two labels and two confounder values are all present, so a confounder effect
cannot be confused with a class-imbalance effect. This is the PathoROB-style design, and
the one the paper reports.
|
|
|
|---|---|---|
|
ignored |
required |
Neighbourhood scope |
the whole retained cohort |
within each subset |
Evaluated once per |
sample |
|
|
|
|
The last row carries a real consequence. Under paired_2x2 a sample may belong to
several subsets and contributes one occurrence to each, so the score averages over
occurrences, not samples. result.n_pairs therefore counts occurrences in one design and
samples in the other, and the two designs are not directly comparable.
paired_2x2¶
Each subset value names one group of rows, and is used only if it forms a complete
2x2: exactly two label values, exactly two confounder values, and at least one sample
in each of the four (label, confounder) cells.
Warning
Subsets that are not complete 2x2 are silently skipped, not reported as errors. If your scores come from fewer occurrences than expected, check subset completeness first. If no subset is complete, the call raises.
A minimal manifest – one subset, four cells, one sample each:
sample_id,image_path,label,group_id,center,subset
s1,/data/1.png,tumor,slide-1,center_a,tumor_vs_normal__a_b
s2,/data/2.png,tumor,slide-2,center_b,tumor_vs_normal__a_b
s3,/data/3.png,normal,slide-3,center_a,tumor_vs_normal__a_b
s4,/data/4.png,normal,slide-4,center_b,tumor_vs_normal__a_b
That is the required shape, not a workable size – see How big must it be?.
A row carries exactly one subset value. To evaluate the same sample inside several 2x2
comparisons, repeat the row once per subset, and repeat its embedding row to match.
scripts/prep/prepare_paired_manifest.py expands a flat manifest into all complete 2x2
combinations for you.
all¶
No subset column. Every row is scored against the whole cohort:
sample_id,image_path,label,group_id,center
s1,/data/1.png,tumor,slide-1,center_a
s2,/data/2.png,tumor,slide-2,center_b
s3,/data/3.png,normal,slide-3,center_a
s4,/data/4.png,normal,slide-4,center_b
How big must it be?¶
Both rules below are about the neighbourhood scope – the subset under paired_2x2,
the whole retained cohort under all.
RI and MaRI. Every candidate k must be strictly less than the number of rows in the
scope; candidates that are not are dropped, and if none survive the call raises
RuntimeError: ... k-selection failed because no valid k candidates remain. With the
default k_candidates=[5, 11, 21] the scope needs at least 6 rows – and a k barely
under the scope size is not a meaningful neighbourhood anyway.
CRoMa. A sample resolves only if m SO and m OS neighbours can be found
for it. With the default m=5, each of the four (label, confounder) cells wants at
least 5 samples, in distinct group_id groups. Unresolved samples are counted in
result.undefined_frac rather than dropped, and a run that resolves none returns nan.
Neither rule can produce a wrong number – RI raises, CRoMa reports what it could not resolve – but an undersized manifest wastes a run.
Deduplicated embeddings¶
Because paired_2x2 repeats rows across subsets, the same image is often embedded more
than once. Embed each unique image once, then expand back to manifest-row order with
croma.expand_features_to_manifest(), which takes the deduplicated embeddings
together with the embedding manifest they were computed from.