radiens_qc
radiens-qc turns a Radiens recording into an answerable question: is this data good enough to use?
Point it at a recording and a :class:~radiens_core.VidereClient; it configures
read-time DSP, walks the recording in windows, and returns a
:class:~radiens_qc.report.QualityReport -- per-channel SNR / noise / event
rate, a quality tier per channel, and a single-letter grade::
from pathlib import Path
from radiens_core import VidereClient
from radiens_qc import compute_report, save_report
vc = VidereClient()
report = compute_report(vc, Path("/data/rec01.xdat"), source="exp/day3")
print(report.grade(), report.median("snr"))
save_report(report, Path("reports"))
The package never learns where recordings are stored: hand it a local path (it
links the file itself -- linking is idempotent) or an already-linked
:class:~radiens_core.models.DatasetMetadata. Fetching bytes from Drive, S3, or
a share stays the caller's job.
Cross-session comparison lives in its own module, so which report you are building is always explicit::
from radiens_qc.compare import ComparativeReport, load_session_summaries
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]
|
KPI metric names in column order (as returned by the server). |
thresholds |
QualityThresholds
|
Quality thresholds used for channel classification. |
channels |
DataFrame
|
One row per channel, indexed by native channel index
( |
window_centers_sec |
NDArray[float64]
|
Window center times, in seconds relative to the recording start. |
matrices |
dict[str, NDArray[float64]]
|
|
dsp_params |
list[DSPParams]
|
Read-time DSP applied before KPI computation (provenance). |
event_threshold_sd |
float | None
|
Event-detection threshold in noise-SD multiples
applied before KPI computation, or |
Methods:
rank_order
Channel positions ordered by descending :data:PRIMARY_METRIC (best first).
The report's canonical display order. Every panel that lists channels -- the ranked bar and both channel x time heatmaps, including the event-rate one -- uses this same array, so a given row is the same physical channel across panels and they stay comparable row-for-row.
Falls back to natural channel order when the report has no
:data:PRIMARY_METRIC column (a metrics set that excluded it). The
panels that consume this already degrade gracefully in that case, so
raising here would defeat their guards.
QualityThresholds
dataclass
Heuristic thresholds for classifying per-channel signal quality.
These are deliberately conservative, adjustable defaults — the KPI SNR is a continuous signal-band/noise-band ratio, not a spike SNR, so tune them to your probe/prep rather than treating them as universal.
Attributes:
| Name | Type | Description |
|---|---|---|
snr_good |
float
|
Channels at or above this SNR are |
snr_fair |
float
|
Channels between |
dead_noise_uv |
float
|
Channels whose noise floor falls below this (µV), or whose
SNR is non-finite, are treated as |
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. |
Functions:
amp_ports
Distinct hardware ports the recording's amplifier channels occupy.
A DSP filter targets one port, so filters must be replicated across every
port present. The port is the leading letter of a channel's hardware-native
name (e.g. "A-000" → port A). Falls back to a single port A if no native
name parses (single-headstage recordings).
apply_kpi_preprocessing
apply_kpi_preprocessing(
vc,
dataset,
*,
dsp_params=DEFAULT_EVENT_DSP,
event_threshold_sd=DEFAULT_EVENT_THRESHOLD_SD,
)
Configure read-time DSP filters and event threshold for KPI compute.
Sets STAGE2 DSP filters (the only stage the KPI engine reads through)
and/or the per-channel event-detection threshold on the datasource, so that
subsequently computed KPI metrics (SNR, event rate, noise, RMS) reflect them.
No data files are rewritten — this is read-time filtering.
The server auto-clears and recomputes the KPI cache when either changes, but
that recompute is asynchronous. Callers must therefore trigger and await KPI
computation (:meth:VidereClient.kpi_calculate) after this returns, before
reading metrics, or get_kpi_metrics may return stale/partial values.
:func:~radiens_qc.compute.compute_report handles that ordering for you.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
vc
|
VidereClient
|
Client connected to the server holding |
required |
dataset
|
DatasetMetadata
|
The linked recording to configure. |
required |
dsp_params
|
Sequence[FlexDSPParams] | None
|
DSP filters applied at |
DEFAULT_EVENT_DSP
|
event_threshold_sd
|
float | None
|
Symmetric event-detection threshold in noise-SD
multiples (applied as |
DEFAULT_EVENT_THRESHOLD_SD
|
compute_report
compute_report(
vc,
data,
*,
source=None,
window_sec=10.0,
metrics=DEFAULT_METRICS,
thresholds=None,
dsp_params=DEFAULT_EVENT_DSP,
event_threshold_sd=DEFAULT_EVENT_THRESHOLD_SD,
)
Compute a quality report for one recording.
Links the recording if needed, optionally configures read-time DSP filters
and an event-detection threshold so the KPIs reflect them, triggers KPI
computation, then walks the recording in window_sec-wide windows,
querying per-channel metrics for each. Whole-recording per-channel values
are the mean across windows; the full per-window matrices are retained for
temporal-stability panels.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
vc
|
VidereClient
|
Client connected to the server that will do the computation. |
required |
data
|
DatasetMetadata | Path
|
An already-linked recording, or a local filesystem path to link.
A path is linked via |
required |
source
|
str | None
|
Provenance string recorded on the report (e.g. the Drive folder or experiment the recording came from). Defaults to the dataset's own path. Never parsed by this package. |
None
|
window_sec
|
float
|
Width of each analysis window, in seconds. |
10.0
|
metrics
|
Sequence[FlexKpiMetricId]
|
KPI metric names to compute (e.g. |
DEFAULT_METRICS
|
thresholds
|
QualityThresholds | None
|
Channel-quality thresholds; defaults to |
None
|
dsp_params
|
Sequence[FlexDSPParams] | None
|
Read-time DSP applied before KPI computation (e.g. a
spike-band bandpass). |
DEFAULT_EVENT_DSP
|
event_threshold_sd
|
float | None
|
Event-detection threshold in noise-SD multiples,
applied before KPI computation. |
DEFAULT_EVENT_THRESHOLD_SD
|
Returns:
| Type | Description |
|---|---|
QualityReport
|
The computed report. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the recording yields no analyzable windows. |
load_report
Load a report previously written by :func:save_report.
Reads folders written by any schema version. Pre-v1 folders (no
schema_version) predate matrices.npz and the drive_path ->
source rename; they load with empty
:attr:~radiens_qc.report.QualityReport.matrices, which makes the temporal
panels blank rather than failing.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
report_dir
|
Path
|
A report directory containing at least |
required |
Returns:
| Type | Description |
|---|---|
QualityReport
|
The reconstructed report. |
plot_report
Build the single-page quality-report figure, sized as a US Letter page.
Layout (6 rows x 3 cols): 0. metadata/scorecard header, spanning all columns. 1. three probe-geometry maps (SNR, noise, activity). 2-3. a ranked per-channel SNR bar spanning a 2x2 block (tall enough to list every site with upright labels), with the noise-floor histogram in the 2x1 column to its right. 4-5. temporal profiles block (array-wide trend line plot, channel x time SNR heatmap, and channel x time firing-dynamics heatmap sharing an x-axis), full width.
The ranked bar and both stability heatmaps share one channel row order
(:meth:~radiens_qc.report.QualityReport.rank_order -- ranked by SNR, best
at top/row 0), so a given row/bar is the same physical channel across all
three panels and they are directly comparable.
.. important::
The figure is laid out at an exact 8.5x11in figsize. Save it with
bbox_inches=None -- matplotlib's savefig default of "tight"
crops to content and destroys the page sizing.
:func:~radiens_qc.persist.save_report does this for you.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
report
|
QualityReport
|
Report to plot. |
required |
Returns:
| Type | Description |
|---|---|
Figure
|
The figure. The caller owns it and is responsible for closing it. |
plot_report_panels
Build each report panel as its own standalone figure.
Emits the same panels the composite page is built from -- header, the three
spatial maps, the ranked SNR bar, the noise histogram, the temporal trend,
and the two channel x time heatmaps -- by reusing the same private
_draw_* functions, so no drawing logic is duplicated. Rows use the same
:meth:~radiens_qc.report.QualityReport.rank_order as the composite, so
panels stay comparable with it and with each other.
Unlike the fixed-page composite, these are sized per panel and are meant to
be saved cropped to content (bbox_inches="tight", matplotlib's default).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
report
|
QualityReport
|
Report to render. |
required |
Returns:
| Type | Description |
|---|---|
dict[str, Figure]
|
Panel name -> figure, e.g. |
dict[str, Figure]
|
|
dict[str, Figure]
|
|
dict[str, Figure]
|
for closing them. |
resolve_dsp_params
Normalize DSP params to what will actually be applied to dataset.
Coerces the DSPParamsDict shorthand into real :class:DSPParams, injects
STAGE2, and replicates any param that does not name a port across every
amplifier port the recording occupies (a DSP filter targets one port, so a
port-less param means "all of them").
A param that names a port explicitly is left on that port -- a caller who targeted one port meant it, and silently fanning it out would apply filtering they did not ask for.
Returning fully-resolved params rather than passing the shorthand straight through lets the report record exactly what was applied, instead of recording the request and hoping it matches.
save_report
Persist report to <out_dir>/<report.base_name>/.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
report
|
QualityReport
|
Report to persist. |
required |
out_dir
|
Path
|
Parent directory; one subdirectory per recording is created. |
required |
figure
|
bool
|
Also render and save the composite report page. |
True
|
panels
|
bool
|
Also render each panel as a separate PNG in a
|
False
|
dpi
|
int
|
Resolution for the rendered figures. |
150
|
Returns:
| Type | Description |
|---|---|
Path
|
The report's own directory. |
screen_dataset
screen_dataset(
dataset,
*,
min_duration_sec,
default_probe_markers=DEFAULT_PROBE_MARKERS,
recorded_after=None,
recorded_before=None,
)
Decide whether a linked recording should be reported on.
Screens the recording's own metadata — cheap checks made after link but before the expensive KPI compute — so junk datasets are skipped without paying for a full report.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dataset
|
DatasetMetadata
|
The linked recording metadata to screen. |
required |
min_duration_sec
|
float
|
Reject recordings shorter than this (too little data for reliable per-window KPIs). |
required |
default_probe_markers
|
Sequence[str]
|
Substrings that mark a placeholder/default probe; a recording using one is unmapped/test data. |
DEFAULT_PROBE_MARKERS
|
recorded_after
|
datetime | None
|
If set, reject recordings whose wall-clock start is before this (or unknown). Must be tz-aware (recording wall time is UTC-aware). |
None
|
recorded_before
|
datetime | None
|
If set, reject recordings whose wall-clock start is at or after this (or unknown). Must be tz-aware. |
None
|
Returns:
| Type | Description |
|---|---|
str | None
|
A short human-readable reason to skip the recording, or |
str | None
|
passes all checks. |