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#

.. py:function:: hungarian_match(A_est, A_true, *, metric=’iou’, energy_frac=0.9) :module: minisim

Optimally pair estimated spatial footprints to true ones by overlap.

Each footprint is binarized to the smallest pixel set holding energy_frac of its energy (see :data:DEFAULT_ENERGY_FRAC), the pairwise IoU (Jaccard) matrix is formed, and :func:scipy.optimize.linear_sum_assignment finds the assignment maximizing total IoU. Pairs with zero overlap are dropped from the returned :attr:Match.pairing.

:param A_est: Footprint stacks (n, height, width), non-negative. Negative values (if any) are clipped to zero before binarizing. :param A_true: Footprint stacks (n, height, width), non-negative. Negative values (if any) are clipped to zero before binarizing. :param metric: Only "iou" is supported in v1; other values raise ValueError. :param energy_frac: Fraction of each footprint’s energy its binary mask retains, in (0, 1].

.. py:class:: Match(iou_matrix, pairing) :module: minisim :canonical: minisim.metrics.Match

Bases: :py:class:object

The result of pairing estimated footprints to true ones by spatial overlap.

pairing is the optimal one-to-one assignment (maximizing total IoU) with pure non-overlapping pairs dropped, so it is safe to feed straight into the temporal metrics. The threshold-dependent quality summaries (:meth:recall, :meth:precision) count only pairs whose IoU clears iou_threshold.

Empty denominators (no estimated or no true cells, no matched pairs) report 0.0 rather than nan - convenient for assert metric >= bound tests.

.. py:method:: Match.matched_pairs(iou_threshold=0.5) :module: minisim

  The assigned pairs whose IoU is at least ``iou_threshold`` (true positives).

.. py:method:: Match.recall(iou_threshold=0.5) :module: minisim

  True positives over the number of true cells (``0.0`` if there are none).

.. py:method:: Match.precision(iou_threshold=0.5) :module: minisim

  True positives over the number of estimated cells (``0.0`` if there are none).

.. py:property:: Match.mean_iou :module: minisim :type: float

  Mean IoU over the matched (positive-overlap) pairs (``0.0`` if none).

Temporal scores#

.. py:function:: trace_pearson(C_est, C_true, pairing) :module: minisim

Per-matched-pair Pearson correlation between estimated and true traces.

Returns one correlation per (est_idx, true_idx) in pairing (a constant trace has undefined correlation and yields nan). C_est/C_true are (unit, frame).

.. py:function:: spike_precision_recall(S_est, S_true, pairing, *, tol_frames=2, spike_thresh=0.0) :module: minisim

Pooled spike-detection precision/recall over the matched units.

A frame is a spike where S > spike_thresh. Within each matched pair, true spikes are greedily matched to the nearest unused estimated spike within ±tol_frames (a true positive); unmatched true spikes are false negatives and unmatched estimated spikes are false positives. Counts are pooled across all pairs, then reduced to precision = TP/(TP+FP) and recall = TP/(TP+FN) (0.0 when a denominator is empty). S_est/S_true are (unit, frame).

.. py:class:: SpikeScore(precision, recall) :module: minisim :canonical: minisim.metrics.SpikeScore

Bases: :py:class:~typing.NamedTuple

Pooled spike-train detection score across all matched units.

.. py:attribute:: SpikeScore.precision :module: minisim :type: float

  Alias for field number 0

.. py:attribute:: SpikeScore.recall :module: minisim :type: float

  Alias for field number 1

Field and motion#

.. py:function:: field_pearson(est, true) :module: minisim

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 nan if either field is constant.

.. py:function:: shift_rmse(shifts_est, shifts_true) :module: minisim

Root-mean-square error (pixels) between two (frame, 2) shift trajectories.

Pure RMSE over all frames and both axes - the caller must put both arrays in the same sign convention. A motion-correction estimate is the negation of the applied GroundTruth.shifts, so negate one before comparing.