Skip to content

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 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]

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 (ntv_idx). Columns: site_num, probe_x, probe_y, name, one column per metric (whole-recording per-channel mean), and quality (good/fair/poor/dead).

window_centers_sec NDArray[float64]

Window center times, in seconds relative to the recording start.

matrices dict[str, NDArray[float64]]

metric -> (n_windows, n_channels) arrays; channel columns are aligned to channels.index order. Empty when a report is loaded from a pre-v1 folder that did not persist them.

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 None if the server default was used (provenance).

Methods:

grade
grade()

Single-letter overall grade (A-D) from good/dead channel fractions.

mean
mean(metric)

Whole-recording mean of metric across channels.

median
median(metric)

Whole-recording median of metric across channels.

quality_counts
quality_counts()

Number of channels in each quality tier (good/fair/poor/dead).

rank_order
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.

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 — 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 "good".

snr_fair float

Channels between snr_fair and snr_good are "fair"; below snr_fair they are "poor".

dead_noise_uv float

Channels whose noise floor falls below this (µV), or whose SNR is non-finite, are treated as "dead" (disconnected/flatline).

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

amp_ports(dataset)

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 dataset.

required
dataset DatasetMetadata

The linked recording to configure.

required
dsp_params Sequence[FlexDSPParams] | None

DSP filters applied at STAGE2, in order. Any param that does not name a port is replicated across every amplifier port the recording occupies; stage is injected. None or empty leaves the DSP group unchanged.

DEFAULT_EVENT_DSP
event_threshold_sd float | None

Symmetric event-detection threshold in noise-SD multiples (applied as [-sd, +sd] on every amplifier channel). None leaves the threshold unchanged.

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 vc.link_data_file; linking is idempotent, so passing a path for a file already linked is free. Note this is deliberately not str -- in radiens-core a bare string means a dataset id, and accepting one here would silently mean something different.

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. "snr", "noise_uv").

DEFAULT_METRICS
thresholds QualityThresholds | None

Channel-quality thresholds; defaults to QualityThresholds().

None
dsp_params Sequence[FlexDSPParams] | None

Read-time DSP applied before KPI computation (e.g. a spike-band bandpass). None computes KPIs on the unfiltered signal.

DEFAULT_EVENT_DSP
event_threshold_sd float | None

Event-detection threshold in noise-SD multiples, applied before KPI computation. None uses the server default.

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_report(report_dir)

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 meta.json and channels.csv.

required

Returns:

Type Description
QualityReport

The reconstructed report.

plot_report

plot_report(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

plot_report_panels(report)

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. "header", "spatial_snr",

dict[str, Figure]

"ranked_bar", "noise_hist", "temporal_trend",

dict[str, Figure]

"snr_time_heatmap". The caller owns the figures and is responsible

dict[str, Figure]

for closing them.

resolve_dsp_params

resolve_dsp_params(dataset, 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

save_report(
    report, out_dir, *, figure=True, panels=False, dpi=150
)

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 <base_name>_panels/ subdirectory.

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 None if it

str | None

passes all checks.