Evaluation metrics¶
Thirteen metrics in four families. All are oriented so that higher is better, and all
are bounded, which is what makes weighted aggregation across them meaningful. All are
computed by scfoundry benchmark.
Biological conservation metrics¶
These ask whether cells of the same type end up near each other. Computed against
obs["cell_type"] on every tissue.
Metric |
Implementation |
What it measures |
|---|---|---|
|
|
Agreement between clusters and true labels over all cell pairs, corrected for chance. |
|
|
Shared information between the cluster partition and the label partition. |
|
|
Does each cluster contain only one cell type? Rewards splitting. |
|
|
Is each cell type confined to one cluster? Rewards merging. The counterweight to |
|
|
Geometric mean of pairwise precision and recall. |
|
|
Average silhouette width over true labels, Euclidean. Compares within-type to between-type distance — the only metric that uses distances directly rather than neighbourhoods or partitions. |
|
|
Cell-type local inverse Simpson’s index, perplexity 30, scaled. High when a cell’s neighbours share its type. |
|
|
Can a cell’s type be predicted from its neighbours? The most directly interpretable metric here. |
|
|
Is each cell type a single connected component in the 15-nearest-neighbour graph, or fragmented? |
Reporting all nine matters because they fail differently. HOM and COM pull in opposite
directions, so an embedding can score well on one by doing badly at the other. High
Acc@kNN with low ARI means local structure is right while global geometry does not
partition cleanly. A single summary metric hides all of this.
Batch-mixing metrics¶
These ask whether cells from different technical batches are interleaved. Computed against
obs["batch_id"] on the 19 multi-batch tissues.
Metric |
Implementation |
What it measures |
|---|---|---|
|
|
Is the batch composition of each local neighbourhood consistent with the global composition? Computed per cell type, α = 0.05, 50 neighbours. |
|
|
Batch-removal-adapted silhouette: silhouette over batch labels, cosine distance, with
between-cluster distances taken as the mean over other clusters. Requires
|
|
built on |
Cell-type-conditioned iLISI: batch LISI computed within each cell type, normalised by that cell type’s own batch count, averaged over cell types with at least 10 cells and at least 2 batches. |
|
|
Integration LISI, perplexity 30, scaled. Computed but excluded from the reported score. |
Important
iLISI is computed and stored but does not enter the batch-mixing family. It and
CiLISI measure nearly the same thing, and including both would double-weight LISI-style
mixing relative to kBET and BRAS. CiLISI is preferred because conditioning on cell
type prevents a method from scoring well simply by mixing everything together.
The conditioning point generalises. Any batch-mixing metric can be maximised by destroying biological structure, which is why batch metrics are never reported alone — the weighted score always includes the three biological families.
Metric families and weights¶
The twelve scored metrics are correlated in blocks. Averaging them equally would give the
five cluster-agreement metrics five times the influence of ASW, purely because there
happen to be five of them. Grouping into families and weighting the families fixes that.
Family |
Weight |
Metrics |
|---|---|---|
Cluster-label concordance |
0.325 |
|
Continuous cell-type separation |
0.10 |
|
Local cell-type neighbourhood structure |
0.325 |
|
Batch mixing |
0.25 |
|
Three quarters of the weight sits on biology, split evenly between partition-level
agreement (0.325) and neighbourhood-level structure (0.325), with continuous geometry
deliberately light at 0.10 — ASW is the most sensitive to embedding scale and
dimensionality, and so the least comparable across methods. Batch mixing takes the
remaining 0.25.
Missing families¶
When a family cannot be computed — the batch family on a single-batch tissue — it is dropped and the remaining weights are renormalised to sum to 1. A single-batch tissue is scored on its three biological families, not penalised for lacking a fourth.
Clustering¶
The five cluster-agreement metrics need a partition, and the choice of clustering changes
the numbers. The paper’s protocol, and the default of scfoundry benchmark:
a 15-nearest-neighbour graph on the embedding, Euclidean; Leiden with
flavor="igraph"andrandom_state=0;resolutions 0.1, 0.2, …, 3.0, extended in steps of 0.5 up to 6.0 while no resolution yet reaches the number of true labels;
the resolution whose cluster count is closest to the number of true labels is selected.
Leiden is graph-based, matching how single-cell data is normally clustered, and does not
assume the isotropic clusters k-means implies. scfoundry benchmark --clustering kmeans
is offered as the simpler alternative — k-means with k equal to the number of true
labels, random_state=1, n_init=10 — and its numbers are not interchangeable with
the Leiden ones. Say which protocol you used; the note column of every long-format table
records it.
The four non-clustering metrics — ASW, cLISI, Acc@kNN, GC — and the batch metrics
use true labels directly and are unaffected by this choice.
Ranking across tissues¶
Raw metric values are not comparable between tissues. A tissue with 32 cell types produces
systematically lower ARI than one with 14, whatever the method. Averaging raw values
across tissues therefore ranks tissue difficulty as much as method quality — and averaging
raw values across metrics lets the metric with the widest numeric range dominate.
The paper therefore works with ranks throughout. Within each tissue:
metric rank rank of the method on that metric among all methods (1 = best; ties averaged)
family rank mean of the metric ranks within the family, over the metrics observed
aggregate rank Σ weight_f × family_rank_f / Σ weight_f over the families observed
and the mean aggregate rank is the mean of a method’s per-tissue aggregate ranks. A method that places second in every tissue beats one that wins three tissues and comes last in the rest — which is the right notion of a generally useful method. All 20 methods, zero-shot and integration alike, are ranked together.
from pathlib import Path
import pandas as pd
# One wide table per method and metric group, as written by `scfoundry benchmark`.
bio = pd.concat(pd.read_csv(p) for p in Path("results/benchmark").glob("*_bio_conservation_leiden_metrics_wide.csv"))
batch = pd.concat(pd.read_csv(p) for p in Path("results/benchmark").glob("*_batch_mixing_leiden_metrics_wide.csv"))
wide = bio.merge(batch, on=["sample_id", "method"], how="left").set_index(["sample_id", "method"])
FAMILIES = {
"cluster_label_concordance": ["ARI", "NMI", "HOM", "COM", "FMI"],
"continuous_cell_type_separation": ["ASW"],
"local_cell_type_neighborhood_structure": ["Acc@kNN", "cLISI", "GC"],
"batch_mixing": ["kBET", "BRAS", "CiLISI"],
}
WEIGHTS = pd.Series({
"cluster_label_concordance": 0.325,
"continuous_cell_type_separation": 0.10,
"local_cell_type_neighborhood_structure": 0.325,
"batch_mixing": 0.25,
})
# 1. Rank every method on every metric within each tissue (higher value = better rank).
metric_rank = wide.groupby(level="sample_id").rank(ascending=False, method="average")
# 2. Average the metric ranks within each family; a family with no observed metric is NaN.
family_rank = pd.DataFrame({f: metric_rank[cols].mean(axis=1) for f, cols in FAMILIES.items()})
# 3. Weighted mean of the family ranks, weights renormalised over the families observed.
present = family_rank.notna()
aggregate = family_rank.mul(WEIGHTS, axis=1).sum(axis=1) / present.mul(WEIGHTS, axis=1).sum(axis=1)
# 4. Average over tissues.
print(aggregate.groupby(level="method").mean().sort_values()) # lower is better
On the Tabula Sapiens v2 tables this reproduces the published ranking to the last digit; the manuscript adds tissue-level bootstrap confidence intervals, Friedman and paired Wilcoxon tests against PCA and scVI, and leave-one-tissue-out stability on top of it.
Reproducing metric values¶
scfoundry benchmark --embedding results/embeddings/scgpt --batch-key batch_id
reproduces every value of the published tables for that method: Leiden protocol, kNN
settings, scib-metrics ≥ 0.5.6 with kbet_per_label at 50 neighbours, bras with cosine
distance and between_cluster_distances="mean_other", and the cell-type-conditioned LISI
for CiLISI. Leiden is deterministic given the graph, so reruns agree to the last digit on
the same software; across scib-metrics or igraph versions expect differences in the
third decimal.
Note
BRAS changed definition across scib-metrics releases. Values computed with anything
older than 0.5.6 are not comparable, and the note column of the long table records the
definition used alongside each value for exactly this reason.
See also
Benchmark — running the metric task.
Benchmark design — the dataset these metrics are computed on.