radiens_qc.report
The per-recording quality report and the thresholds that classify its channels.
Deliberately free of matplotlib: :class:QualityReport is a data object that
:mod:radiens_qc.plot_quality renders and :mod:radiens_qc.persist serializes, not
something that knows how to draw or save itself.
Classes
QualityReport
dataclass
Self-contained quality report for a single recording.
Attributes:
| Name | Type | Description |
|---|---|---|
base_name |
str
|
Recording identifier. |
source |
str
|
Caller-supplied provenance string describing where the recording came from (a Drive path, an experiment id, a bare filesystem path). Opaque to this package -- it is recorded and displayed, never parsed. |
recorded_at |
datetime | None
|
Wall-clock recording start (UTC), or |
duration_sec |
float
|
Recording duration in seconds. |
sampling_rate |
float
|
Sampling rate in Hz. |
num_channels |
int
|
Number of amplifier channels analyzed. |
probe_ids |
list[str]
|
Distinct probe identifiers present. |
headstage_ids |
list[str]
|
Distinct headstage identifiers present. |
window_sec |
float
|
Window width used for the temporal analysis. |
metrics |
list[str]
|
Metric names in column order -- the ones the server returned,
followed by the ratios :func: |
thresholds |
QualityThresholds
|
Quality thresholds used for channel classification. |
channels |
DataFrame
|
One row per channel, indexed by native channel index
( The two are deliberately not symmetric. |
window_centers_sec |
NDArray[float64]
|
Window center times, on the dataset's own time base, which does not generally start at zero. |
detection |
ResolvedDetection
|
Stage-2 filters and detection threshold the KPIs were measured
through (provenance). Required, because |
matrices |
dict[str, NDArray[float64]]
|
|
Methods:
ap_band_rms_uv
Median spike-band RMS across channels (µV), or None if not measurable.
A whole-recording noise judgment, and the one that catches what the
per-channel outlier test structurally cannot: a uniformly noisy recording
has no outliers, because every channel is equally bad. Compare against
:attr:QualityThresholds.ap_band_rms_max_uv.
Returns None unless a bandpass appears in the applied filters. The
rms metric is only a spike-band RMS because the KPI engine read
through a bandpass; with no filter established this is the RMS of
whatever the server happened to be applying, which is not a quantity any
threshold describes.
crest_factor
Median peak-excursion-over-noise across channels, or None if not measured.
A whole-recording artifact reading: movement, stim and chewing all put a sample far above what the channel's quiet samples do, and this is how far. Built from two ungated metrics, so unlike SNR it still reports through the epochs an offset makes detection unreliable.
Only comparable against a stated window width. Its numerator is an
extremum, which grows with the width of the fold, so the same recording
read at one window and at another does not give the same number --
:attr:window_sec is the width this one was measured at.
depth_order
Channel positions ordered by shank, then depth.
The report's one display order. Every panel that lists channels -- the per-channel profiles and every channel x time heatmap -- uses this array, so a given row is the same physical site across all of them and against the site maps, which draw the same probe.
Depth rather than metric rank because channel defects are spatially
structured: a damaged shank, a bad connector block and a lifted probe
each occupy a contiguous run of sites, and any metric ordering scatters
exactly that signature across the panel. See
:func:~radiens_qc._dataset.depth_order.
event_counts
Number of channels on which the detector did and did not fire (present/absent).
in_event_fraction
Median share of samples falling inside a detected event, or None.
Near zero on a normally spiking channel: an event occupies a millisecond or two and they are seconds apart. A channel well above that is not spiking -- it is ringing, oscillating, or detecting against a threshold that has collapsed onto its own noise.
integrity_counts
Number of channels in each electrode-integrity tier (ok/noisy/dead).
noise_cutoff_uv
Noise floor (µV) above which this report graded a channel noisy.
The bar the integrity column was actually held to, recoverable from
the report rather than left inside the classifier -- provenance for the
grade, in the same sense as :attr:detection.
Returns None when the report has no noise column, or when no channel
was live and there is therefore no cutoff to state.
shank_ids
Shank of each channel, numbered from the leftmost, aligned to channels.index.
The grouping :meth:depth_order sorts by, for callers that compare sites
within a shank. See :func:~radiens_qc._dataset.shank_ids.
QualityThresholds
dataclass
Heuristic thresholds for classifying per-channel signal quality.
These are deliberately conservative, adjustable defaults — tune them to your probe and preparation rather than treating them as universal.
Attributes:
| Name | Type | Description |
|---|---|---|
dead_noise_uv |
float
|
Channels whose noise floor falls below this (µV) are
treated as |
noisy_mad_multiplier |
float
|
How many robust standard deviations above the
array's median noise floor a channel must sit to be flagged
|
noisy_scale_floor_frac |
float
|
Lower bound on that robust scale, as a fraction of the array's median noise floor. Keeps the cutoff from collapsing onto the median when more than half the channels share an identical value (quantized readings, or a near-uniform probe), which would otherwise flag every channel above it. Relative rather than absolute so it carries no assumption about a particular rig's noise level. |
ap_band_rms_max_uv |
float
|
Upper bound on the array's median spike-band RMS
(µV), a whole-recording judgment rather than a per-channel one: it
catches a uniformly noisy recording, which by definition has no
outliers for |
snr_scale_min |
float
|
Lower bound of the fixed SNR color scale used by the spatial map and stability heatmap. |
snr_scale_max |
float
|
Upper bound of the fixed SNR color scale used by the spatial map and stability heatmap. These scales are intentionally not autoscaled to each report's own min/max -- a probe that's uniformly excellent would otherwise still show "red" at the low end of its own compressed range, giving a false impression of poor sites. |
noise_scale_min_uv |
float
|
Lower bound of the fixed noise-floor color scale used by the spatial map. |
noise_scale_max_uv |
float
|
Upper bound of the fixed noise-floor color scale used by the spatial map, for the same reason. |
event_rate_scale_max_hz |
float
|
Upper bound of the fixed activity/event-rate color scale used by the spatial map, for the same reason. |
amplitude_scale_max_uv |
float
|
Upper bound of the fixed mean-event-amplitude color scale, for the same reason. |
crest_scale_max |
float
|
Upper bound of the fixed peak-over-noise color scale.
A clean spike-band channel sits a few noise SDs up, since detection
itself runs at |
in_event_scale_max |
float
|
Upper bound of the fixed in-event-fraction color scale. Ordinary spiking occupies well under a percent of a window -- a millisecond or two per event, seconds apart -- so a scale ending here spends its range where the interesting values are. |
Functions:
classify_events
Record whether the detector fired on each channel (present/absent).
A measurement rather than a grade: absent is a normal state for an
electrode that is simply not near a firing neuron, and carries no severity.
It is only interpretable alongside integrity -- absent on a dead
channel says nothing about the preparation.
The comparison is written to be NaN-safe rather than guarded: the server
reports EvtRate = EvtK / DurSec, a finite 0.0 when nothing was
detected, but blanks it to NaN when detection itself was unreliable.
NaN > 0 is False, so both cases fall to absent.
classify_integrity
Grade each electrode ok/noisy/dead from its noise floor alone.
Reads :data:NOISE_METRIC and nothing else. In particular it never reads
:data:PRIMARY_METRIC: the server reports SNR as NaN whenever no events
were detected, so grading on it condemns a healthy electrode that simply
recorded nothing. noise_uv is never gated server-side and needs no
events, which makes it the one measurement that stays honest on such a
channel.
The two tests are asymmetric on purpose: :func:dead_mask is absolute,
while :func:noise_cutoff is an outlier test against the rest of this same
probe.
Pure over the channel table, so a report reloaded from disk can be regraded against different thresholds without recomputing anything on the server.
dead_mask
Channels whose noise floor says no electrode is on the other end.
An absolute test, unlike :func:noise_cutoff: a floor at or near zero means
the same thing on any probe, and a non-finite one means the server could not
measure it at all.
mask_non_finite
Replace non-finite entries with NaN so nan-aware aggregates ignore them.
The KPI server has two no-data sentinels for a window in which nothing was
measured, and normalizing both to NaN here is what lets every aggregate
downstream be a plain nan-aware call. An event metric such as
event_max_abs reports -inf for a window with no detected events (an
accumulator never overwritten because no spikes were found), while snr
and noise_uv report NaN.
np.nanmean/nanmedian/nanpercentile strip only NaN, so a single
-inf window left as-is would drag an entire whole-recording or
whole-window aggregate to -inf even when every other window has a
well-defined value.
Lives here rather than in :mod:radiens_qc.compute because it encodes how
:attr:QualityReport.matrices is written, so both the module that builds
those matrices and the module that plots them read the rule from one place.
noise_cutoff
Noise floor above which a live channel is graded noisy.
An outlier test against the rest of this probe rather than an absolute bar -- it answers "is this electrode worse than its neighbors", which is what a bad connector pin or a damaged site looks like, and there is no universal number for that.
The spread is the median absolute deviation, not the sample standard deviation. The channels being detected are in the sample the spread is computed from, and squaring deviations lets them inflate an SD until the cutoff rises past their own values -- the test would fail exactly as the probe got worse. The MAD only breaks down past 50% contamination, and the 1.4826 factor makes the two agree on clean Gaussian data.
Dead channels are excluded first: a probe half full of disconnected sites near 0 µV would drag the median down far enough to flag every working one.
Returns inf when no channel is live, so nothing is an outlier against a
population that does not exist.