Skill Tracer
Skills › Scientific Agent Skills › neuropixels-analysis › QUALITY_METRICS.md

QUALITY_METRICS.md

120 lines · skills/neuropixels-analysis/references/QUALITY_METRICS.md at 5f2bc03

Open on GitHub
1# Quality metrics and screening
2
3Targets SpikeInterface 0.105.0; reviewed 2026-10-01. An apparently clean unit is
4not proof of a single neuron. Inspect waveforms, refractory behavior, stability,
5spike counts, artifacts and neighboring units together.
6

Quality metrics and screening

Targets SpikeInterface 0.105.0; reviewed 2026-10-01. An apparently clean unit is not proof of a single neuron. Inspect waveforms, refractory behavior, stability, spike counts, artifacts and neighboring units together.

Raw lines 1 to 6
7## Compute metrics with their dependencies
8
9```python
10import spikeinterface.full as si
12analyzer = si.create_sorting_analyzer(sorting, recording, sparse=True)
13analyzer.compute("random_spikes", max_spikes_per_unit=500, seed=42)
14analyzer.compute("waveforms", ms_before=1.0, ms_after=2.0)
15analyzer.compute("templates", operators=["average", "std"])
16analyzer.compute("noise_levels")
17analyzer.compute("spike_amplitudes")
18analyzer.compute("amplitude_scalings")
19analyzer.compute("quality_metrics",
20 metric_names=["firing_rate", "snr", "isi_violation", "presence_ratio", "amplitude_cutoff"],
21 metric_params={"isi_violation": {"isi_threshold_ms": 1.5, "min_isi_ms": 0.0},
22 "presence_ratio": {"bin_duration_s": 60.0}},
24qm = analyzer.get_extension("quality_metrics").get_data()
25```
27The default presence bin is 60 s: shorter recordings return NaN. Short smoke
28fixtures may use shorter bins, but this changes the scientific quantity. An
29amplitude cutoff can also be NaN when too few spikes support the histogram.
30Never fill these values with a passing score. The bundled classifiers leave any
31missing/nonfinite required metric `unsorted`.

Compute metrics with their dependencies

import spikeinterface.full as si

analyzer = si.create_sorting_analyzer(sorting, recording, sparse=True)
analyzer.compute("random_spikes", max_spikes_per_unit=500, seed=42)
analyzer.compute("waveforms", ms_before=1.0, ms_after=2.0)
analyzer.compute("templates", operators=["average", "std"])
analyzer.compute("noise_levels")
analyzer.compute("spike_amplitudes")
analyzer.compute("amplitude_scalings")
analyzer.compute("quality_metrics",
    metric_names=["firing_rate", "snr", "isi_violation", "presence_ratio", "amplitude_cutoff"],
    metric_params={"isi_violation": {"isi_threshold_ms": 1.5, "min_isi_ms": 0.0},
                   "presence_ratio": {"bin_duration_s": 60.0}},
)
qm = analyzer.get_extension("quality_metrics").get_data()

The default presence bin is 60 s: shorter recordings return NaN. Short smoke fixtures may use shorter bins, but this changes the scientific quantity. An amplitude cutoff can also be NaN when too few spikes support the histogram. Never fill these values with a passing score. The bundled classifiers leave any missing/nonfinite required metric unsorted.

Raw lines 7 to 32
33## Metric names differ from result columns
35Pass the left column to `metric_names`; query the returned right-hand columns.
36Inspect `si.get_quality_metric_list()` and `si.get_default_quality_metrics_params()`
37for the installed release. Version 0.105 uses nested `metric_params`.
39| Metric name | Result column(s) | Additional requirement |
40|---|---|---|
41| `isi_violation` | `isi_violations_ratio`, `isi_violations_count` | Spike times, recording duration |
42| `amplitude_cv` | `amplitude_cv_median`, `amplitude_cv_range` | Spike amplitudes, enough spikes/bins |
43| `drift` | `drift_ptp`, `drift_std`, `drift_mad` | `spike_locations` extension |
44| `mahalanobis` | `isolation_distance`, `l_ratio` | `principal_components` extension |
45| `nearest_neighbor` | `nn_hit_rate`, `nn_miss_rate` | Principal components |
46| `silhouette` | Method-dependent silhouette column | Principal components |
47| `synchrony` | `sync_spike_2`, `sync_spike_4`, `sync_spike_8` by default | Spike times |
48| `snr` | `snr` | Templates and noise levels |
49| `amplitude_cutoff` | `amplitude_cutoff` | `amplitude_scalings` (current preferred dependency) |
50| `presence_ratio` | `presence_ratio` | Explicit bin duration |
51| `firing_rate` | `firing_rate` | Duration in seconds |
53```python
54analyzer.compute("principal_components", n_components=3, mode="by_channel_local")
55analyzer.compute("spike_locations", method="center_of_mass")
56analyzer.compute("quality_metrics", metric_names=["mahalanobis", "nearest_neighbor", "drift"],
57 metric_params={"nearest_neighbor": {"n_neighbors": 4}}, n_jobs=1)
58```
60NaN or inf from sparse clusters, rank-deficient PCA, too few spikes, or too little
61recording is a reason to inspect the data, not a threshold to relax automatically.
62Do not compute every expensive metric by default.

Metric names differ from result columns

Pass the left column to metric_names; query the returned right-hand columns. Inspect si.get_quality_metric_list() and si.get_default_quality_metrics_params() for the installed release. Version 0.105 uses nested metric_params.

Metric nameResult column(s)Additional requirement
isi_violationisi_violations_ratio, isi_violations_countSpike times, recording duration
amplitude_cvamplitude_cv_median, amplitude_cv_rangeSpike amplitudes, enough spikes/bins
driftdrift_ptp, drift_std, drift_madspike_locations extension
mahalanobisisolation_distance, l_ratioprincipal_components extension
nearest_neighbornn_hit_rate, nn_miss_ratePrincipal components
silhouetteMethod-dependent silhouette columnPrincipal components
synchronysync_spike_2, sync_spike_4, sync_spike_8 by defaultSpike times
snrsnrTemplates and noise levels
amplitude_cutoffamplitude_cutoffamplitude_scalings (current preferred dependency)
presence_ratiopresence_ratioExplicit bin duration
firing_ratefiring_rateDuration in seconds
analyzer.compute("principal_components", n_components=3, mode="by_channel_local")
analyzer.compute("spike_locations", method="center_of_mass")
analyzer.compute("quality_metrics", metric_names=["mahalanobis", "nearest_neighbor", "drift"],
                 metric_params={"nearest_neighbor": {"n_neighbors": 4}}, n_jobs=1)

NaN or inf from sparse clusters, rank-deficient PCA, too few spikes, or too little recording is a reason to inspect the data, not a threshold to relax automatically. Do not compute every expensive metric by default.

Raw lines 33 to 63
64## Interpret the measurements
66- ISI violation **ratio is rate-normalized**, not the fraction of all spikes that
67 violate a refractory period and not a percentage contamination estimate. It can
68 exceed 1. The chosen refractory/censor windows depend on physiology and sorter
69 duplicate handling; 1.5 ms is a convention, not a universal neuronal constant.
70- Presence ratio is the fraction of bins meeting an activity criterion. It depends
71 on bin size, rate and duration; absence may be real stimulus/state dependence.
72- Amplitude cutoff estimates missing spikes under distribution assumptions. Drift,
73 multimodality and censoring can make it misleading. It is not a direct recall measurement.
74- SNR is template amplitude divided by estimated channel noise; it does not measure
75 isolation by itself. Use matched physical scaling and preprocessing.
76- Drift from sorted spikes can be confounded by activity changes and incorrect
77 assignments. Spatial extent of different neurons is not temporal drift.
78- Waveform shape alone does not establish molecular cell type or somatic origin.

Interpret the measurements

  • ISI violation ratio is rate-normalized, not the fraction of all spikes that violate a refractory period and not a percentage contamination estimate. It can exceed 1. The chosen refractory/censor windows depend on physiology and sorter duplicate handling; 1.5 ms is a convention, not a universal neuronal constant.
  • Presence ratio is the fraction of bins meeting an activity criterion. It depends on bin size, rate and duration; absence may be real stimulus/state dependence.
  • Amplitude cutoff estimates missing spikes under distribution assumptions. Drift, multimodality and censoring can make it misleading. It is not a direct recall measurement.
  • SNR is template amplitude divided by estimated channel noise; it does not measure isolation by itself. Use matched physical scaling and preprocessing.
  • Drift from sorted spikes can be confounded by activity changes and incorrect assignments. Spatial extent of different neurons is not temporal drift.
  • Waveform shape alone does not establish molecular cell type or somatic origin.
Raw lines 64 to 79
80## Institutional criteria versus local presets
82The AllenSDK Visual Coding tutorial documents default filtering at
83`amplitude_cutoff < 0.1`, `presence_ratio > 0.9`, `isi_violations < 0.5`.
84SpikeInterface names the last output `isi_violations_ratio`. Allen data releases
85and pipelines can use different metrics/parameters; state exactly what was used.
87The bundled `allen` preset adds SNR > 3 and is therefore **Allen-inspired**. The
88legacy `ibl` name is a local tighter screen, **not** the IBL classifier. IBL's
89`brainbox.metrics.single_units.quick_unit_metrics` uses sliding-RP, noise-cutoff
90and amplitude criteria; its pass indicator and contamination measures are not
91interchangeable with SpikeInterface's Hill ISI ratio.
93All three bundled entry points use the same screening behavior:
95| Local preset | SNR > | Presence > | ISI ratio < | Amplitude cutoff < | Rate > |
96|---|---:|---:|---:|---:|---:|
97| `allen` | 3 | 0.9 | 0.5 | 0.1 | — |
98| `ibl` (legacy name) | 4 | 0.9 | 0.1 | 0.1 | 0.1 Hz |
99| `strict` | 5 | 0.95 | 0.01 | 0.01 | — |
101Boundaries fail strict inequalities. Finite SNR < 1.5 is flagged `noise`; excess
102ISI ratio is flagged `mua` (strict preset uses > 0.05 for this flag); otherwise
103failed screens remain `unsorted`, including low completeness without contamination.
104These are screening labels to review, not validated diagnoses.
106```python
107# Run from the skill root so bundled helpers are importable.
108import sys
109sys.path.insert(0, "scripts")
110from compute_metrics import curate_units
111labels = curate_units(qm, method="allen")
112good_ids = [uid for uid, label in labels.items() if label == "good"]
113```
115Keep all metrics and labels, selected IDs, parameter values, package versions,
116recording duration, and a review rationale. Never discard the uncurated result.
118Sources: [SpikeInterface API](https://spikeinterface.readthedocs.io/en/stable/api.html),
119[AllenSDK quality metrics](https://allensdk.readthedocs.io/en/latest/_static/examples/nb/ecephys_quality_metrics.html),
120[IBL single-unit metrics](https://docs.internationalbrainlab.org/_autosummary/brainbox.metrics.single_units.html).

Institutional criteria versus local presets

The AllenSDK Visual Coding tutorial documents default filtering at amplitude_cutoff < 0.1, presence_ratio > 0.9, isi_violations < 0.5. SpikeInterface names the last output isi_violations_ratio. Allen data releases and pipelines can use different metrics/parameters; state exactly what was used.

The bundled allen preset adds SNR > 3 and is therefore Allen-inspired. The legacy ibl name is a local tighter screen, not the IBL classifier. IBL's brainbox.metrics.single_units.quick_unit_metrics uses sliding-RP, noise-cutoff and amplitude criteria; its pass indicator and contamination measures are not interchangeable with SpikeInterface's Hill ISI ratio.

All three bundled entry points use the same screening behavior:

Local presetSNR >Presence >ISI ratio <Amplitude cutoff <Rate >
allen30.90.50.1—
ibl (legacy name)40.90.10.10.1 Hz
strict50.950.010.01—

Boundaries fail strict inequalities. Finite SNR < 1.5 is flagged noise; excess ISI ratio is flagged mua (strict preset uses > 0.05 for this flag); otherwise failed screens remain unsorted, including low completeness without contamination. These are screening labels to review, not validated diagnoses.

# Run from the skill root so bundled helpers are importable.
import sys
sys.path.insert(0, "scripts")
from compute_metrics import curate_units
labels = curate_units(qm, method="allen")
good_ids = [uid for uid, label in labels.items() if label == "good"]

Keep all metrics and labels, selected IDs, parameter values, package versions, recording duration, and a review rationale. Never discard the uncurated result.

Sources: SpikeInterface API, AllenSDK quality metrics, IBL single-unit metrics.

Raw lines 80 to 120