Recovery metrics#
Functions to score an analysis pipeline’s output against the ground truth. See the benchmarking guide for how they fit together.
Spatial matching#
- minisim.hungarian_match(A_est, A_true, *, metric='iou', energy_frac=0.9, shift=None)[source]#
Optimally pair estimated spatial footprints to true ones by overlap.
Forms the pairwise footprint-similarity matrix under
metricand runsscipy.optimize.linear_sum_assignment()to find the assignment maximizing total similarity. Pairs with zero similarity are dropped from the returnedMatch.pairing.- Parameters:
A_est – Footprint stacks
(n, height, width), non-negative. Negative values (if any) are clipped to zero before scoring.A_true – Footprint stacks
(n, height, width), non-negative. Negative values (if any) are clipped to zero before scoring.metric (str) – Footprint similarity (see
SIMILARITY_METRICS):"iou"(binary energy-mask Jaccard, the default),"cosine", or"weighted_jaccard". The two weighted metrics compare the intensity profile, so pixel weights - not just which pixels are lit - drive the match.energy_frac (float) – For
metric="iou"only: fraction of each footprint’s energy its binary mask retains, in(0, 1].shift (tuple[float, float] | str | None) – Global translation applied to
A_estbefore scoring, to absorb a uniform offset between the estimate’s (motion-corrected) frame and the true frame.Noneapplies nothing; a(dy, dx)tuple applies that known shift (rounded to whole pixels - derive it from the motion trajectories);"auto"estimates one by aligning the intensity-weighted centroids of the two footprint stacks. The applied integer shift is recorded onMatch.shift.
- Return type:
- class minisim.Match(similarity_matrix, pairing, metric='iou', shift=(0, 0))[source]#
Bases:
objectThe result of pairing estimated footprints to true ones by spatial overlap.
pairingis the optimal one-to-one assignment (maximizing total similarity) with pure non-overlapping pairs dropped, so it is safe to feed straight into the temporal metrics. The threshold-dependent quality summaries (recall(),precision()) count only pairs whose similarity clearsthreshold.metricrecords which similarity was used (seeSIMILARITY_METRICS) andshiftthe global(dy, dx)translation applied to the estimate before scoring ((0, 0)when none).iou_matrix/mean_iouremain as aliases for the genericsimilarity_matrix/mean_similarityso existing callers keep working; they hold the chosen metric, not necessarily IoU.Empty denominators (no estimated or no true cells, no matched pairs) report
0.0rather thannan- convenient forassert metric >= boundtests.- Parameters:
- matched_pairs(threshold=0.5)[source]#
The assigned pairs whose similarity is at least
threshold(true positives).
- recall(threshold=0.5)[source]#
True positives over the number of true cells (
0.0if there are none).
- precision(threshold=0.5)[source]#
True positives over the number of estimated cells (
0.0if there are none).
- property mean_similarity: float#
Mean similarity over the matched (positive-overlap) pairs (
0.0if none).
- property mean_iou: float#
Alias for
mean_similarity(the chosenmetric, not always IoU).
Temporal scores#
- minisim.trace_pearson(C_est, C_true, pairing)[source]#
Per-matched-pair Pearson correlation between estimated and true traces.
Returns one correlation per
(est_idx, true_idx)inpairing(a constant trace has undefined correlation and yieldsnan).C_est/C_trueare(unit, frame).- Return type:
- minisim.activity_similarity(S_est, S_true, pairing)[source]#
Scale-aware recovery of the deconvolved activity over the matched units.
For each matched pair the estimated activity
S_est[i]is compared to the true activityS_true[j]((unit, frame)arrays) without binarizing: the deconvolved estimate is a continuous activity rate, related to the truth by an unknown non-negative amplitude factor, so the comparison must be invariant to that scale. Per pair this returns (seeActivityScore):the Pearson
correlation(scale- and offset-invariant shape match);the recovered non-negative
scalealpha minimizing|| S_true - (alpha * S_est + beta) ||(the best amplitude map,alpha >= 0);the
variance_explainedby that fit,1 - SS_res / SS_tot.
A constant estimate yields
scale = 0andvariance_explained = 0(it explains nothing); a constant truth yieldsnanfor both correlation and variance explained (there is nothing to explain). This is the deconvolution scorecard CaLab uses (per-cell amplitude alpha plus proportion of variance explained), not a spike-timing precision/recall.- Return type:
- class minisim.ActivityScore(correlation, scale, variance_explained)[source]#
Bases:
NamedTuplePer-matched-pair recovery of the deconvolved activity (see
activity_similarity()).The deconvolved estimate (CNMF/minian
S) is not a spike train: it is a non-negative estimate of neural activity rate, scaled by an unknown factor that maps activity to calcium-kernel amplitude. So each pair is scored without binarizing and without assuming a common scale:correlation- Pearson r between estimated and true activity (scale- and offset-invariant; the shape match).nanfor a constant trace.scale- the recovered non-negative amplitude factor alpha that best maps the estimate onto the true activity (the unknown gain, made explicit).variance_explained- proportion of the true activity’s variance the non-negatively scaled estimate accounts for, in(-inf, 1].nanwhen the true activity is constant.
Field and motion#
- minisim.field_pearson(est, true)[source]#
Pearson correlation between two 2-D fields (vignette, leakage), flattened.
Scale- and offset-invariant, so it scores the shape of the recovered field rather than its absolute level. Returns
nanif either field is constant.- Return type:
- minisim.shift_rmse(shifts_est, shifts_true, *, correction=False, align=False)[source]#
Root-mean-square error (pixels) between two
(frame, 2)shift trajectories.RMSE over all frames and both axes. The two arrays must share a sign convention: a motion-correction estimate is the negation of the applied
GroundTruth.shifts, so passcorrection=Trueto negateshifts_estfirst (the common case, since a motion-correction pipeline emits the shift it would apply to undo the motion); leave itFalseto compare two trajectories already in the same convention.They must also share an origin, and that is the catch across pipelines: a correction trajectory is relative to whatever template the pipeline registered to, so it can carry an arbitrary constant per-axis offset versus minisim’s zero-shift reference.
align=Trueremoves the best constant offset (the per-axis mean residual) before the RMSE, scoring how well the motion was tracked rather than which frame was called zero. Leave itFalseto hold the pipeline to the absolute origin too.
- minisim.global_shift_from_trajectories(shifts_est, shifts_true, *, correction=True)[source]#
The constant
(dy, dx)offset between two motion trajectories, in pixels.The estimated correction and the true applied motion track the same brain movement, so their difference is (up to noise) a constant: the offset between the pipeline’s registration template and minisim’s zero-shift reference. That constant is exactly the global translation between the estimated and true footprint frames, so it can be read off the trajectories rather than searched for. Returns the per-axis mean difference rounded to whole pixels, ready to pass as
hungarian_match()’sshift.correction=Truenegates the estimate first (the usual convention, matchingshift_rmse()).