Skip to content

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 None if unknown.

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:~radiens_qc.compute.derive_ratios folded from them.

thresholds QualityThresholds

Quality thresholds used for channel classification.

channels DataFrame

One row per channel, indexed by native channel index (ntv_idx). Columns: site_num, probe_x, probe_y, name, one column per metric (whole-recording per-channel mean), plus integrity and events.

The two are deliberately not symmetric. integrity (ok/noisy/dead) is a grade: tiers with a severity ordering, describing whether the electrode is working. events (present/absent) is a measurement: whether the detector crossed threshold, with no severity ordering, and absent is a normal state rather than a defect. events is only interpretable alongside integrity — a dead channel reporting absent says nothing about the preparation.

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 event_rate is only interpretable against a stated criterion.

matrices dict[str, NDArray[float64]]

metric -> (n_windows, n_channels) arrays; channel columns are aligned to channels.index order.

Methods:

ap_band_rms_uv
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
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
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
event_counts()

Number of channels on which the detector did and did not fire (present/absent).

in_event_fraction
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
integrity_counts()

Number of channels in each electrode-integrity tier (ok/noisy/dead).

median
median(metric)

Whole-recording median of metric across channels.

noise_cutoff_uv
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_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.

summary_stats
summary_stats(metric)

Distribution stats (min/p25/median/mean/p75/max/std) for metric.

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 "dead" (disconnected/flatline). A floor for detecting disconnection, not a noise-quality bar.

noisy_mad_multiplier float

How many robust standard deviations above the array's median noise floor a channel must sit to be flagged "noisy". Compared against a MAD-derived scale rather than the sample standard deviation, which the outliers being detected inflate enough to hide themselves — see :func:noise_cutoff.

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 noisy_mad_multiplier to find. The default follows published inclusion guidance for in-vivo extracellular arrays and is a starting point, not a universal — a different probe, species, or preparation may warrant a different bar.

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 threshold_sd; well past that is a channel taking an excursion no spike accounts for.

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

classify_events(channels)

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

classify_integrity(channels, thresholds)

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

dead_mask(noise_uv, thresholds)

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

mask_non_finite(matrix)

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_cutoff(noise_uv, thresholds)

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.