spacr.run_compare¶
Two runs, side by side: what changed, how many fewer, which hits moved.
A screen is re-run with one setting nudged and the numbers come out different. Nothing in spaCR has ever answered why: the settings that produced a result were not recorded next to it, the object counts were never compared, and the hit list was a CSV you diffed by eye. So the honest answer to “is this better than last week’s run?” was to open two folders and squint.
This module answers it, in three parts, from
spacr.artifacts — the registry every output now registers with —
rather than from a filesystem scan:
- Settings diff
Which parameters moved, grouped by the same headings the settings panel groups them by.
spacr.qt.settings_diffdoes the work.- Count diff
Objects, wells and fields, per plate and overall. A run that produced 12% fewer cells is the single most useful early signal that something changed for the worse, and it is visible long before anyone looks at a p-value.
- Hit-list diff
For runs that produced regression results: which hits appeared, which vanished, and which merely moved. Rank churn among a stable hit set matters as much as set membership — a screen whose top ten reshuffle every run is not a screen anyone should publish from.
Not every pair of runs can be compared. Different plates, a module one
run never ran, a different spaCR version: comparability() says so
in words, and compare_runs() refuses to produce the three tables
rather than presenting a misleading one. A version difference is
deliberately a warning and not a blocker — it does not stop the
comparison, but it has to be on screen, because a Cellpose upgrade
between two runs can account for a count change on its own.
Public API¶
runs_inGroup a project’s registered artifacts into
RunRefruns.comparabilityWhether two runs may be compared, and what to say if not.
compare_runsAll three diffs, or the reason there are none.
count_database,diff_countsThe count half on its own, for a caller holding two database paths.
read_hits,diff_hitsThe hit-list half on its own, for two result CSVs.
Attributes¶
Classes¶
Whether two runs may be put side by side, and what to say about it. |
|
The count comparison: overall first, then one block per plate. |
|
One count, on both sides. |
|
One reason two runs are hard, or impossible, to compare. |
|
One row of a hit list. |
|
What happened to one key between two hit lists. |
|
Which hits appeared, which vanished, and which merely moved. |
|
One run's hits, ranked. |
|
Two runs and the three ways they differ. |
|
What one run actually produced, counted out of its database. |
|
One run, assembled from the artifacts it registered. |
Functions¶
|
Decide whether two runs may be compared, and say why not. |
|
Compare two runs: settings, counts and hit list. |
|
Count objects, wells and fields in one measurements database. |
|
Line two count tables up, overall first and then per plate. |
|
Compare two ranked hit lists: membership, then rank churn. |
|
Read one hit list off a results CSV. |
|
Group everything a project registered into runs, newest first. |
Module Contents¶
- class spacr.run_compare.Comparability[source]¶
Whether two runs may be put side by side, and what to say about it.
- Parameters:
comparable – no blocking finding was raised.
findings – everything found, blockers first.
shared_modules – modules both runs ran.
shared_kinds – output kinds both runs produced.
shared_plates – plates both runs touched. Empty when neither run names any, which is not the same as “no overlap”.
- class spacr.run_compare.CountDiff[source]¶
The count comparison: overall first, then one block per plate.
- Parameters:
rows – every metric on both sides, unchanged ones included — a count that did not move is evidence, unlike a setting that did not move.
a – the baseline counts.
b – the comparison counts.
- class spacr.run_compare.CountRow[source]¶
One count, on both sides.
- Parameters:
scope –
"overall"or a plate id.metric –
"cell","wells", …a – the baseline count, or
Nonewhen that side has no such metric at all — which is not the same as zero.b – the comparison count, or
None.
- class spacr.run_compare.Finding[source]¶
One reason two runs are hard, or impossible, to compare.
- Parameters:
code – machine-readable, e.g.
"version-changed".severity –
BLOCKINGorWARNING.message – one sentence, written for the user.
- class spacr.run_compare.Hit[source]¶
One row of a hit list.
- Parameters:
key – the thing being called — a gene, a gRNA, a feature.
rank – 1-based position; see
read_hits()for the ordering.score – the effect size it was ranked on, when there was one.
- class spacr.run_compare.HitChange[source]¶
What happened to one key between two hit lists.
- Parameters:
key – the gene / gRNA / feature.
status –
"appeared","vanished","moved"or"held".a_rank – rank in the baseline, or
None.b_rank – rank in the comparison, or
None.a_score – effect size in the baseline, when there was one.
b_score – effect size in the comparison.
- class spacr.run_compare.HitDiff[source]¶
Which hits appeared, which vanished, and which merely moved.
- Parameters:
changes – every key from either list, in a stable order: appeared, vanished, then the shared ones by how far they moved.
a – the baseline list.
b – the comparison list.
- property churn: float[source]¶
Fraction of the shared hits whose rank moved, 0.0–1.0.
The number that says a screen is unstable even when its hit set is not: identical membership with a reshuffled top ten scores 1.0 here and 0 appeared / 0 vanished up there.
- property moved: Tuple[HitChange, Ellipsis][source]¶
Keys in both lists whose rank changed, biggest move first.
Keys both runs called.
- class spacr.run_compare.HitList[source]¶
One run’s hits, ranked.
- Parameters:
path – the CSV they were read from.
available – whether it could be read.
note – why not, when it could not.
hits – the rows, best rank first.
key_column – the column the keys came from.
score_column – the column the ranking used, or
""when the file’s own order was kept.
- class spacr.run_compare.RunComparison[source]¶
Two runs and the three ways they differ.
- Parameters:
a – the baseline run.
b – the run being compared to it.
comparability – the verdict and its reasons.
settings – the settings diff, or
Nonewhen the runs are not comparable and the caller did not force it.counts – the count diff, same rule.
hits – the hit-list diff, same rule.
forced – the caller asked for the diffs despite a blocker.
- comparability: Comparability[source]¶
- settings: spacr.qt.settings_diff.SettingsDiff | None = None[source]¶
- class spacr.run_compare.RunCounts[source]¶
What one run actually produced, counted out of its database.
- Parameters:
path – the database it was counted from.
available – whether it could be read.
note – why not, when it could not.
overall – metric → count over the whole run.
per_plate – plate → metric → count.
- class spacr.run_compare.RunRef[source]¶
One run, assembled from the artifacts it registered.
A run is what a
spacr.artifacts.Artifact.run_idgroups. An artifact registered without one is its own single-output run rather than being dropped: an output nobody stamped is still an output somebody may want to compare against.- Parameters:
run_id – the run id, or
"artifact:<id>"for an unstamped one.project – the project root the outputs belong to.
modules – producing modules, sorted.
kinds – output kinds, sorted.
plates – plates the run touched, sorted. Derived from its settings; the count diff finds the real ones in the database.
spacr_version – the version that produced the outputs. Empty when unrecorded;
"mixed"when the run’s artifacts disagree.settings – the material settings, from the newest artifact that carries any.
settings_hash – that run’s settings digest.
created_ns – the newest artifact’s registration time.
created_utc – the same instant, ISO-8601.
status – the worst status any of its artifacts recorded.
artifacts – the artifacts themselves, newest first.
- artifact_of(kind: str) Any | None[source]¶
The newest artifact of
kindthis run produced, orNone.- Parameters:
kind – a
spacr.portskind, e.g."measurements-db".
- spacr.run_compare.comparability(a: RunRef, b: RunRef, *, a_plates: Sequence[str] | None = None, b_plates: Sequence[str] | None = None) Comparability[source]¶
Decide whether two runs may be compared, and say why not.
Blocking, because diffing past them produces a table that is worse than no table:
no shared module — the runs did different things, so there is no output of the same kind to line up.
no shared output kind — the modules overlap but what they actually produced does not.
different plates — both runs name plates and the sets are disjoint. Counting objects across two different experiments and calling the difference a regression is exactly the misleading table this check exists to prevent.
Warnings, because they change what the numbers mean but the comparison is still worth seeing:
version-changed — different spaCR versions. Called out on its own: a segmentation change between versions moves object counts with no setting having moved, so a count delta must never be read without it.
different project, partial plate overlap, modules only one run ran, a run that did not finish.
- Parameters:
a – the baseline run.
b – the run being compared to it.
a_plates – the plates the baseline really touched, when the caller knows better than
RunRef.platesdoes — which it does whenever it has counted the database.b_plates – the same for the compared run.
- Returns:
a
Comparability;bool()of it is the verdict.
- spacr.run_compare.compare_runs(a: RunRef, b: RunRef, *, include_same_settings: bool = False, force: bool = False) RunComparison[source]¶
Compare two runs: settings, counts and hit list.
Refuses by default when
comparability()raises a blocker. That is the point of the check — two runs of different plates can be subtracted from one another, and the resulting table looks exactly like a regression report, which is why it must not be drawn without the user having said they know what they are doing.- Parameters:
a – the baseline run, from
runs_in().b – the run being compared to it.
include_same_settings – carry the settings both runs agree on, for the “show everything” view.
force – produce the diffs even when the runs are not comparable.
- Returns:
a
RunComparison. When it refused,RunComparison.comparableis False and the diffs areNone;comparability.blockerssays why.
- spacr.run_compare.count_database(path: str | os.PathLike | None) RunCounts[source]¶
Count objects, wells and fields in one measurements database.
Deliberately tolerant: a database missing
pathogenis a run that segmented no pathogens, not an error, and a run whose database has been deleted must report that rather than raise — the comparison screen has to say why half the table is empty.- Parameters:
path – the
measurements.dbto count.- Returns:
a
RunCounts;availableis False with anotewhen there was nothing to count.
- spacr.run_compare.diff_counts(a: RunCounts, b: RunCounts) CountDiff[source]¶
Line two count tables up, overall first and then per plate.
- Parameters:
a – the baseline counts.
b – the comparison counts.
- Returns:
a
CountDiff. Rows are emitted for every metric either side reported, so a table that vanished between runs shows asNoneon one side rather than being absent.
- spacr.run_compare.diff_hits(a: HitList, b: HitList) HitDiff[source]¶
Compare two ranked hit lists: membership, then rank churn.
- Parameters:
a – the baseline list.
b – the comparison list.
- Returns:
a
HitDiff.
- spacr.run_compare.read_hits(path: str | os.PathLike | None, *, key_column: str = '', score_column: str = '', limit: int | None = None) HitList[source]¶
Read one hit list off a results CSV.
The rank is defined here, once, so both sides of a diff are ranked the same way: rows are sorted by descending absolute score, ties broken by key. Absolute, because a screen’s strongest hit is its largest effect in either direction and ranking a strong protective hit below a weak sensitising one would report rank churn that is an artefact of the sort. When no score column can be found the file’s own row order is kept and
HitList.score_columnis empty, so a caller can tell a real ranking from a preserved one.- Parameters:
path – a
results*.csvfrom the regression module, or any CSV with a name column.key_column – force the key column instead of detecting it.
score_column – force the score column. Pass
"-"to rank by file order even when a score column exists.limit – keep only the top
limitrows after ranking.
- Returns:
a
HitList;availableis False with anotewhen there was nothing to read.
- spacr.run_compare.runs_in(registry: Any, project: str | os.PathLike | None = None, *, limit: int | None = None) List[RunRef][source]¶
Group everything a project registered into runs, newest first.
This is how the comparison screen fills its two dropdowns. It reads
spacr.artifacts.Registry.by_project()rather than walking the filesystem, so a run whose outputs have since been deleted still appears — with the settings that produced them — instead of silently vanishing from the list of things you can compare against.- Parameters:
registry – a
spacr.artifacts.Registry.project – the project root;
Nonemeans the registry’s own,""means every project in a shared registry file.limit – keep only the newest
limitruns.
- Returns:
RunRefobjects, newest first.