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 the stage-2 filters and detection threshold, walks the recording in windows, and returns a :class:~radiens_qc.report.QualityReport -- per-channel SNR / noise / event rate, an electrode-integrity grade, and whether the detector fired::

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.integrity_counts())   # {"ok": 15, "noisy": 1, "dead": 0}
print(report.event_counts())       # {"present": 9, "absent": 7}
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.

Classes

DetectionSettings dataclass

What to configure on a datasource before measuring it.

The two travel together because they are never independently meaningful: a threshold is in multiples of a noise floor measured through the filters, and an event_rate states nothing without both. Passing one object is also what keeps a report and the raw-signal figures drawn beside it describing the same signal, which two loose arguments left to a caller to keep in sync.

Attributes:

Name Type Description
dsp_params Sequence[DSPParam] | None

Filters applied at stage 2, 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 datasource's DSP group untouched, whatever it currently holds.

threshold_sd float

Symmetric event-detection threshold in noise-SD multiples, applied as [-sd, +sd] on every amplifier channel. Always applied.

Methods:

resolve
resolve(dataset)

Normalize to what will actually be applied to dataset.

EventTrains dataclass

Every threshold crossing in one recording, per channel.

Attributes:

Name Type Description
base_name str

Recording identifier.

sampling_rate float

Sampling rate in Hz.

time_range_sec tuple[float, float]

(start, end) on the dataset's own time base, which does not generally start at zero.

channel_indices tuple[int, ...]

Amplifier ntv_idx values read, ascending.

geometry DataFrame

Per-channel probe geometry, indexed by ntv_idx.

crossings dict[int, NDArray[float64]]

ntv_idx -> crossing times in seconds on that same time base. A channel that never fired is absent rather than empty, which is why :meth:times exists.

detection ResolvedDetection

Stage-2 filters and threshold the crossings were found at.

spikes_id str

Fileset the crossings were read from. Carried so a caller wanting waveforms for these same events reads the fileset that produced them rather than whichever the dataset offers by default.

Attributes

num_events property
num_events

Total crossings across every channel.

Methods:

depth_order
depth_order()

Row order for the raster: shank, then depth.

See :func:~radiens_qc._dataset.depth_order. This level takes depth rather than the SNR rank the report's panels share, because what it is drawn to show -- an array-wide stripe, a burst confined to one shank -- is spatial structure that a quality ranking scatters.

times
times(ntv_idx)

Crossing times on one channel, empty when it never fired.

The distinction between a silent channel and an unread one is carried by :attr:channel_indices, not by the presence of a key -- so a figure can draw an empty row for a channel that was measured and found silent.

Excerpt dataclass

One slice of the recording, read twice through different filter stages.

wideband and filtered are (n_channels, n_samples) in the channel order of the owning :class:RawSignal, and cover the same samples: the pair is what makes "did the DSP chain do what we think" answerable.

FigureSources dataclass

The objects the figures are drawn from, however the caller came by them.

Every field is optional because which ones are needed depends on what was selected, and because a report read off disk carries no live recording. :func:render skips a figure whose source is absent rather than failing: drawing what a saved report can support is a normal thing to do.

Methods:

has
has(source)

Whether this holds the object a figure declaring source needs.

FigureSpec dataclass

One figure: what it is called, what it shows, and how to draw it.

Attributes:

Name Type Description
name str

Unique across the whole registry, not merely within a level -- selection and the rendered mapping are both keyed by it, so two figures sharing a name could not be asked for separately.

level Level

Which level of the data the figure shows.

source Source

Which of the package's objects :attr:build draws from.

build _Build

Draws the figure from a :class:FigureSources and a :class:RenderOptions.

caption str

One line saying what the reader is looking at.

needs_waveforms bool

Whether the figure reports unit amplitude, which is measured from spike waveforms and has no other source. What decides whether :func:fetch_figure_sources pays for the largest download the package makes.

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.

RawSignal dataclass

Excerpts, crossings, geometry and whole-file spectra for raw-signal figures.

Attributes:

Name Type Description
base_name str

Recording identifier.

sampling_rate float

Sampling rate in Hz.

duration_sec float

Recording duration in seconds.

channel_indices tuple[int, ...]

Amplifier ntv_idx per row of every excerpt array.

geometry DataFrame

Per-channel probe geometry, indexed by ntv_idx.

excerpts tuple[Excerpt, ...]

One per entry of :data:EXCERPT_FRACTIONS, in time order.

crossings dict[int, NDArray[float64]]

ntv_idx -> crossing times in seconds on the dataset's own time base, covering the excerpts only. Empty when crossings were not requested; a channel that fired in no excerpt is simply absent.

detection ResolvedDetection

Stage-2 filters and detection threshold the excerpts were read through and the crossings found at.

freqs NDArray[float64]

Frequency axis in Hz for whole-file spectral estimates.

psd_wideband NDArray[float64]

Whole-file wideband Welch power spectral density, (n_channels, n_freqs) in µV²/Hz.

psd_filtered NDArray[float64]

Whole-file stage-2 filtered Welch power spectral density, (n_channels, n_freqs) in µV²/Hz.

Methods:

depth_order
depth_order()

Row order that puts spatially adjacent sites next to each other.

See :func:~radiens_qc._dataset.depth_order.

window_crossings
window_crossings(
    excerpt, ntv_idx, window_sec=TRACE_WINDOW_SEC
)

Crossing times on ntv_idx inside the drawn window, on the dataset's own time base.

window_slice
window_slice(excerpt, window_sec=TRACE_WINDOW_SEC)

Sample slice of the centered window_sec of excerpt.

window_times
window_times(excerpt, window_sec=TRACE_WINDOW_SEC)

Sample times of the drawn window, on the dataset's own time base.

RenderOptions dataclass

Drawing choices a run makes, for the figures that have one to make.

Reaches every figure; one with no choice to make ignores it. That is what keeps a run-wide choice from having to be repeated per row, and what lets a row be added without a caller learning a new argument.

Attributes:

Name Type Description
smooth_sec float | None

Gaussian time kernel, in seconds, for the figures drawn from the report's window series -- the stability trend, signal_integrity and the channel x window heatmaps. None leaves each its own default bandwidth, which scales with the duration drawn and so differs between two recordings of different lengths; pinning one width is how a set of recordings is made comparable. A width narrower than the report's window is widened to it, and 0 draws the data unfiltered.

The rasters deliberately do not take it. Their kernel is already fixed in seconds rather than scaled to the span, so they need no pinning to compare, and their panel exists to show bursts -- a width suiting a drift readout smears the structure they are drawn for. A raster bandwidth is set by calling :func:~radiens_qc.plot_events.plot_event_raster or :func:~radiens_qc.plot_units.plot_unit_raster with smooth_sec.

ResolvedDetection dataclass

What was applied to a particular recording, recorded as provenance.

Distinct from :class:DetectionSettings so that a report describes the filters that ran rather than the request that produced them: ports are expanded, stage is injected, and the mapping form is gone. An empty dsp_params means no DSP was configured, so the measurement rode on whatever the server already held -- which is why :meth:~radiens_qc.report.QualityReport.ap_band_rms_uv declines to answer.

SortedUnits dataclass

Every unit a clustered sort found in one recording.

Attributes:

Name Type Description
base_name str

Recording identifier.

sampling_rate float

Sampling rate in Hz.

time_range_sec tuple[float, float]

(start, end) on the dataset's own time base, which does not generally start at zero.

geometry DataFrame

Per-channel probe geometry, indexed by ntv_idx. Held so a figure can draw the sites a unit sits among, not because a unit belongs to one.

units DataFrame

One row per unit, indexed by integer label, carrying the primary site, spike count and rate, SNR, and probe coordinates. Those coordinates are the primary site's, copied: the sorter reports no position of its own, so they carry nothing site_ntv_idx does not.

spikes dict[int, NDArray[float64]]

label -> spike times in seconds on that same time base.

spikes_id str

Fileset the units were read from, so a caller wanting waveforms reads them from the same sort.

waveforms dict[int, UnitFootprint]

label -> UnitFootprint, empty until :meth:with_waveforms attaches what :func:fetch_unit_waveforms read. Empty is a normal state, not a failure: the figures that show amplitude say so rather than drawing a field of zeros.

Attributes

labels property
labels

Unit labels, in the row order :attr:units holds them.

Methods:

amplitudes_uv
amplitudes_uv()

label -> µV peak of each unit's mean waveform at its detecting site.

Measured from the waveforms because the sorter reports no amplitude of its own. The same quantity the channel level maps as event amplitude -- the mean absolute peak of an event, at the site that saw it best -- so the two share a color scale and a unit map can be read against the site map beneath it.

Peak of the mean rather than peak-to-peak: peak-to-peak is roughly twice as large, and drawing it against the channel level's scale would put every unit at the top of a ramp that means something else.

Empty when no waveforms are attached, and a unit whose footprint omits its own detecting site is skipped rather than reported at a neighbor's amplitude.

depth_order
depth_order()

Row order for a unit raster: by where the cell sits, not by label.

A unit's probe coordinates are its primary site's, copied -- the sorter reports no position of its own -- so units sharing a site tie exactly. Descending spike count breaks the tie, which puts the site's dominant cell first and matches how :func:_unit_table picks the listing every other per-unit field is read from.

times
times(label)

Spike times for one unit, empty when it has none in the read span.

with_waveforms
with_waveforms(waveforms)

A copy carrying waveforms, as read by :func:fetch_unit_waveforms.

Separate from the fetch so the read stays a read: waveforms are the largest payload the package moves, and whether to pay for them is the caller's decision, not something reading the units settles.

UnitFootprint dataclass

One unit's spikes as the probe saw them, across a neighborhood at once.

Attributes:

Name Type Description
detecting_ntv_idx int

The site these spikes were detected on -- the one that saw the unit most, among the sites that detected it at all.

site_ntv_idx list[int]

The site each column of :attr:waveforms was recorded on, in row order, starting with detecting_ntv_idx. Neighbors are ordered by distance from the detecting site rather than by position on the probe.

waveforms NDArray[float64]

(spikes, sites, wfm_pts), one window per site per spike.

spike_count int

Spikes the unit fired on the detecting site over the read span, before any sampling -- so a sampled footprint still reports what it is a sample of.

Functions:

amp_ports

amp_ports(dataset)

Distinct hardware ports the recording's amplifier channels occupy, in channel order.

A DSP filter targets one port, so filters must be replicated across every port present. Falls back to port A when no channel reports one.

apply_detection_settings

apply_detection_settings(
    vc, dataset, settings=DEFAULT_DETECTION
)

Configure dataset's stage-2 filters and detection threshold, and report what was applied.

No data files are rewritten -- stage 2 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_series may return stale/partial values. :func:~radiens_qc.compute.compute_report handles that ordering for you.

Returns:

Type Description
ResolvedDetection

The settings as applied, for recording as provenance.

cached_key

cached_key(report_dir)

The cache key save_report recorded here, or None if there is none.

None for a directory that does not exist, holds no meta.json, or was written without a key -- all of which read as "cannot be reused".

check_crossings_fileset

check_crossings_fileset(
    vc, dataset, spikes_id, *, ntv_idxs, threshold_sd
)

Refuse a fileset whose crossings are not the per-site events the report counted.

Raises:

Type Description
ValueError

If the fileset detected sites together, at another threshold on any site, or without some of ntv_idxs.

check_units_fileset

check_units_fileset(vc, dataset, spikes_id, *, ntv_idxs)

Refuse a fileset that no sort labeled, or that lacks a requested channel.

Raises:

Type Description
ValueError

If the fileset's provenance has no sort, or it lacks some of ntv_idxs.

compute_or_load_report

compute_or_load_report(
    vc,
    data,
    out_dir,
    *,
    force=False,
    source=None,
    window_sec=None,
    channels=None,
    metrics=DEFAULT_METRICS,
    thresholds=None,
    detection=DEFAULT_DETECTION,
)

Load out_dir's report for this recording, or compute and save it.

A hit still applies thresholds to the loaded report, since thresholds are not part of the key -- so changing a grading bar regrades in-process instead of costing a recompute.

Parameters:

Name Type Description Default
vc VidereClient

Client connected to the server that would do the computation.

required
data DatasetMetadata | Path

An already-linked recording, or a path to link. Linking is idempotent and is the one server call a cache hit still makes; it is what yields the identity the key is built from.

required
out_dir Path

Parent directory holding one subdirectory per recording, as :func:~radiens_qc.persist.save_report writes it.

required
force bool

Recompute and overwrite even when the key matches.

False
source str | None

See :func:~radiens_qc.compute.compute_report. Outside the key: a provenance label this package never measures through.

None
window_sec float | None

See :func:~radiens_qc.compute.compute_report. Outside the key -- pass force when changing it.

None
channels Sequence[int] | None

See :func:~radiens_qc.compute.compute_report. Outside the key -- pass force when changing it.

None
metrics Sequence[FlexKpiMetricId]

See :func:~radiens_qc.compute.compute_report. Outside the key -- pass force when changing it.

DEFAULT_METRICS
thresholds QualityThresholds | None

Channel-classification thresholds, applied on load as well as on compute.

None
detection DetectionSettings

Stage-2 filters and detection threshold. Part of the key.

DEFAULT_DETECTION

Returns:

Type Description
QualityReport

The loaded or freshly computed report.

compute_report

compute_report(
    vc,
    data,
    *,
    source=None,
    window_sec=None,
    channels=None,
    metrics=DEFAULT_METRICS,
    thresholds=None,
    detection=DEFAULT_DETECTION,
)

Compute a quality report for one recording.

Links the recording if needed, selects the channels to analyze, configures the stage-2 filters and detection threshold so the KPIs reflect them, triggers KPI computation, then reads the whole per-channel metric timeseries in one request. Whole-recording per-channel values are the mean across windows; the full per-window matrices are retained for temporal-stability panels.

Leaves channels selected on the datasource when it returns. Selection is shared server-side state, so other clients viewing the same datasource see the change -- as they already do for the DSP group and detection threshold this configures.

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

Requested width of each analysis window, in seconds. The server bins its KPI packets, so actual windows are quantized to the packet duration and the last one may be shorter; a trailing window under half the resolved width is dropped rather than averaged in. None (the default) uses the dataset's native KPI packet duration -- one window per packet, the finest granularity the server has on hand. This is a choice of temporal resolution, not of value: every metric the report aggregates is an intensive quantity, so a narrower window yields more and noisier windows rather than systematically different per-channel numbers.

None
channels Sequence[int] | None

Amplifier ntv_idx values to analyze. None (the default) analyzes every amplifier channel, so the report's counts are against the probe's full site count. Whatever is passed becomes the datasource's amp selection, which is what makes it the report's denominator; a caller narrowing this to its own notion of good channels is where screening policy belongs.

None
metrics Sequence[FlexKpiMetricId]

KPI metric names to compute (e.g. "snr", "noise_uv").

DEFAULT_METRICS
thresholds QualityThresholds | None

Channel-classification thresholds; defaults to QualityThresholds().

None
detection DetectionSettings

Stage-2 filters and detection threshold to measure through, applied before KPI computation and recorded on the report. Hand the same object to :func:~radiens_qc.raw.fetch_raw_signal so the raw-signal figures show the signal the report graded.

DEFAULT_DETECTION

Returns:

Type Description
QualityReport

The computed report.

Raises:

Type Description
ValueError

If channels names an index the recording does not have, if the server returns a different channel set than was selected, or if the recording yields no analyzable windows.

fetch_events

fetch_events(
    vc,
    data,
    spikes_id,
    *,
    channels=None,
    detection=DEFAULT_DETECTION,
)

Read every threshold crossing in the recording from a detect-only fileset.

Parameters:

Name Type Description Default
vc VidereClient

Client connected to the server holding the recording.

required
data DatasetMetadata | Path

An already-linked recording, or a path to link.

required
spikes_id str

Detect-only fileset to read, detected at detection's threshold.

required
channels Sequence[int] | None

Amplifier ntv_idx values to read. None (the default) reads the datasource's current selection, which is the population compute_report measured. Whatever is passed becomes the selection, as it does for a report.

None
detection DetectionSettings

Stage-2 filters and detection threshold. Pass the same object given to :func:~radiens_qc.compute.compute_report.

DEFAULT_DETECTION

Returns:

Type Description
EventTrains

The crossings, with the geometry needed to order them by depth.

Raises:

Type Description
ValueError

If no amplifier channels are selected, or the fileset does not hold per-site crossings at detection's threshold for every channel.

fetch_figure_sources

fetch_figure_sources(
    vc,
    dataset,
    specs,
    *,
    report,
    detection=DEFAULT_DETECTION,
    crossings_spikes_id=None,
    units_spikes_id=None,
)

Fetch what specs need from the recording, each source at most once.

A source no selected figure asks for is not fetched: each is several requests and tens of megabytes. Without crossings_spikes_id the events source is left out and the raw traces carry no crossings; without units_spikes_id the units source is left out. :func:render skips what is missing.

Waveforms are fetched with the units whenever a selected figure reports amplitude. There is no other source for it.

Parameters:

Name Type Description Default
report QualityReport

The measurement the report-backed figures are drawn from. Computed by the caller, since how it is cached is the caller's business -- see :func:~radiens_qc.cache.compute_or_load_report.

required
detection DetectionSettings

Filters and threshold to read the raw signal and events at. Pass the settings the report was computed with, or the figures describe a differently-detected recording than the report beside them.

DEFAULT_DETECTION
crossings_spikes_id str | None

Detect-only fileset for the raw and events levels; see :mod:radiens_qc.spikes.

None
units_spikes_id str | None

Clustered fileset for the units level.

None

fetch_raw_signal

fetch_raw_signal(
    vc,
    data,
    *,
    channels=None,
    fractions=EXCERPT_FRACTIONS,
    excerpt_sec=EXCERPT_SEC,
    detection=DEFAULT_DETECTION,
    crossings_spikes_id=None,
    with_spectra=True,
)

Read paired wideband/filtered excerpts, crossings, and whole-file Welch spectra.

Configures read-time DSP and the detection threshold first, so the filtered excerpt is the signal the KPI engine measured. Whole-file Welch spectra are queried via radiens-core's spectral analysis API for both wideband and stage-2 filtered signals.

Parameters:

Name Type Description Default
vc VidereClient

Client connected to the server holding the recording.

required
data DatasetMetadata | Path

An already-linked recording, or a path to link.

required
channels Sequence[int] | None

Amplifier ntv_idx values to read. None (the default) reads the datasource's current selection, which is the population compute_report measured -- so the figures describe the same array the report graded rather than a wider one. Whatever is passed becomes the selection, as it does for a report.

None
fractions Sequence[float]

Where in the recording to place excerpts.

EXCERPT_FRACTIONS
excerpt_sec float

Seconds fetched per excerpt.

EXCERPT_SEC
detection DetectionSettings

Stage-2 filters and detection threshold to read through. Pass the same object given to :func:~radiens_qc.compute.compute_report.

DEFAULT_DETECTION
crossings_spikes_id str | None

Detect-only fileset to mark crossings from, detected at detection's threshold; see :mod:radiens_qc.spikes. None leaves :attr:RawSignal.crossings empty.

None
with_spectra bool

Whether to fetch whole-file Welch spectra via radiens-core. False skips it, leaving spectral arrays empty.

True

Returns:

Type Description
RawSignal

The fetched excerpts, crossings, and whole-file spectra.

Raises:

Type Description
ValueError

If the recording is shorter than :data:MIN_DURATION_SEC, the server returns a different channel count than was requested, or crossings_spikes_id does not hold per-site crossings at the threshold.

fetch_unit_waveforms

fetch_unit_waveforms(
    vc, dataset, units, *, sample=WAVEFORM_SAMPLE
)

Each unit's spikes, each one carrying every site that saw it.

Read by channel rather than by neuron. A neuron-keyed read attributes a whole channel's rows to every neuron on it, so a site carrying two cells reports the same waveforms for both. Reading by channel and selecting the rows whose label is the unit's keeps a unit's spikes its own.

A spike is kept only on the site that saw its largest peak, and it carries that site's detection neighborhood alongside. Taking one detecting site's spikes therefore gives a footprint whose traces are all the same spikes seen in different places -- which is what makes the spatial spread readable. Pooling across detecting sites would not: those are disjoint sets of spikes, so each site's mean would be over different events, and a unit whose peak wandered would read as one that was wide.

The detecting site is the one that saw the unit most. A unit is listed under several only when its extremum moved between them, which happens to the marginal, low-count units rather than the well-isolated ones.

Read from the fileset the units came from. Sampling bounds the payload: a unit firing at 20 Hz for half an hour has 36,000 spikes, and a mean is converged long before that.

Returns:

Type Description
dict[int, UnitFootprint]

label -> UnitFootprint, for every unit with waveforms in the read span.

fetch_units

fetch_units(vc, data, spikes_id, *, channels=None)

Read every unit a clustered sort found in the recording.

Parameters:

Name Type Description Default
vc VidereClient

Client connected to the server holding the recording.

required
data DatasetMetadata | Path

An already-linked recording, or a path to link.

required
spikes_id str

Clustered fileset to read.

required
channels Sequence[int] | None

Amplifier ntv_idx values to read. None (the default) reads the datasource's current selection.

None

Returns:

Type Description
SortedUnits

The units, with the site geometry needed to draw the probe they sit on.

Raises:

Type Description
ValueError

If no amplifier channels are selected, or the fileset is detect-only or lacks a selected channel.

load_report

load_report(report_dir, *, thresholds=None)

Load a report previously written by :func:save_report.

Parameters:

Name Type Description Default
report_dir Path

A report directory as written by :func:save_report.

required
thresholds QualityThresholds | None

Regrade the channel table against these instead of the ones the report was saved with. Classification is pure over the stored metrics, so this costs no server work -- which is why thresholds are not part of the cache key.

None

Returns:

Type Description
QualityReport

The reconstructed report.

plot_channel_profiles

plot_channel_profiles(report)

Every site's SNR, event amplitude, noise floor and event rate, one row per site.

Bars carry the integrity tier rather than event detection, because integrity is the axis with a severity ordering. A shared row order is what lets an unremarkable SNR be read as a weak signal or a large noise floor.

plot_event_raster

plot_event_raster(
    events, *, span_sec=None, columns=None, smooth_sec=None
)

Raster over per-channel rate traces, on one shared time axis.

Stacked rather than overlaid: the two carry different units, so co-locating them needs a twin y axis that leaves neither labelable, and puts the rate lines over the channels being inspected exactly during the burst being inspected. Sharing x is what makes the comparison work, and both panels order channels by depth and color them alike, so a row in one is findable in the other.

Parameters:

Name Type Description Default
events EventTrains

The fetched crossings.

required
span_sec tuple[float, float] | None

Time range to draw. None draws the whole recording.

None
columns int | None

Time bins across the panel. None uses one per horizontal pixel at the drawn width.

None
smooth_sec float | None

Kernel the rate traces are estimated with, in seconds. Fixed rather than derived from the span, for the reason the module docstring gives. Never narrower than one bin, and 0 draws the rates unfiltered; the rate axis names whichever width was used.

None

Returns:

Type Description
Figure

The figure, with the raster above and the rate lines below.

plot_metric_over_time

plot_metric_over_time(report, metric, *, smooth_sec=None)

One channel x window heatmap, rows in depth order.

metric names one of the report's metrics, and only those with a color scale defined can be drawn -- :data:~radiens_qc.figures.FIGURES holds one entry per such metric. smooth_sec pins the Gaussian kernel along time, in seconds; the default sizes it from the duration drawn (see :func:~radiens_qc._smooth.time_sigma_sec). A pinned width narrower than the report's window is widened to it -- the windows are what the metric was measured over, so nothing finer is there to draw -- and 0 draws the image window for window. Either way the time axis names the width applied.

Raises:

Type Description
KeyError

If no heatmap scale is defined for metric.

plot_raw_spectra

plot_raw_spectra(raw)

Per-channel power spectra of the wideband recording, overlaid and as a site map.

Computed over the full recording on the wideband signal via radiens-core's spectral API: the filtered signal has the notch and bandpass already applied, so line noise is absent from it by construction and a spectrum of it would show the filter rather than the recording. The filtered median is drawn alongside as one line, which is what makes the passband visible.

The left panel overlays every channel, where a line-noise peak and its harmonics stand out. The right panel is the same data as channel x frequency in depth order, where a site unlike its neighbors does.

plot_raw_traces

plot_raw_traces(raw, *, window_sec=TRACE_WINDOW_SEC)

Wideband and stage-2-filtered traces, side by side, one row per excerpt.

The two columns share their channel axis so a site sits at the same height in both -- seeing what the DSP chain removed is the comparison, and it only reads if the pair lines up. Each column carries its own amplitude scale: wideband and filtered legitimately differ by an order of magnitude, and one shared scale would flatten the filtered column to a line.

Crossing ticks go on the filtered column only, since that is the signal detection ran on.

plot_signal_integrity

plot_signal_integrity(report, *, smooth_sec=None)

Artifact and detector saturation over time, as two stacked channel x window rows.

One figure rather than two because the pair is read together: a burst of peak excursion with no rise in in-event samples is an artifact the detector rode out, while the two rising together is the detector firing on it. A shared x-axis, so a band in one panel lines up with the same instant in the other.

smooth_sec pins the Gaussian kernel along time, in seconds; the default sizes it from the duration drawn. A pinned width narrower than the report's window is widened to it, and 0 draws both rows window for window.

plot_site_maps

plot_site_maps(report)

SNR, noise floor, event rate and event amplitude at each site, on the probe's lattice.

One figure rather than four: the maps share a row axis, so reading a site's four values off the same height is the point of drawing them together. Sized from the site lattice, so a two-column probe yields narrow ribbons and a planar array wide panels, at the same width per site.

plot_stability

plot_stability(report, *, smooth_sec=None)

Every array-wide metric over time, each against its own opening value.

smooth_sec pins the Gaussian kernel, in seconds; the default sizes it from the duration drawn (see :func:~radiens_qc._smooth.time_sigma_sec). A pinned width narrower than the report's window is widened to it, and 0 draws the trend unfiltered.

plot_unit_footprints

plot_unit_footprints(units)

Every unit's footprint on the site lattice: where the cell is, and how it fades.

A panel per unit, in depth order, each one the probe's own site lattice cropped around the site that detected the unit. Cell color carries the unit's amplitude at that site and the waveform drawn inside carries its shape, so a panel answers where the cell sits, what it looks like, and how far across the array it was still visible.

The lattice is what makes that readable on any probe. Space is spent per site rather than per micron, so a stagger, a shank gutter and a planar grid all give a waveform the same box to be drawn in, and the fade is read across true adjacency rather than down one column.

Each panel is scaled to its own unit and states its µV per cell, so a weak cell is legible as a shape instead of being drawn as the flat line one shared scale would make of it. Each also carries a locator: the whole probe as one strip, with the crop and the detecting site marked on it, which is what says where on the array the panel is looking.

Every unit gets a panel. The waveforms are the ones attached to units by :meth:~radiens_qc.units.SortedUnits.with_waveforms; a unit with none among them is drawn as an empty panel rather than skipped, so the grid stays a census of the sort.

plot_unit_inventory

plot_unit_inventory(units)

How many cells the sort found, and how their rate, amplitude and SNR are spread.

Descriptive rather than a grade: it says what is here, and leaves whether that is enough to the person who knows what they were recording.

Amplitude is measured from the waveforms attached to units; with none attached that one panel says "not reported" rather than drawing a single bar at zero.

plot_unit_raster

plot_unit_raster(
    units, *, span_sec=None, columns=None, smooth_sec=None
)

Unit rasters over per-unit rate traces, on one shared time axis.

The same figure the events level draws, with a unit per row instead of a site. Rows are ordered by unit depth, so a burst confined to one stretch of the probe still reads as a contiguous band.

smooth_sec pins the rate kernel exactly as it does for :func:~radiens_qc.plot_events.plot_event_raster, so the two stay comparable.

plot_unit_yield

plot_unit_yield(units)

Where on the array cells were found, how clean they are, and how fast they fire.

The only units figure that is array-wide. The others are per-unit -- a footprint panel, a raster row, a histogram bar -- so which stretches of the probe produced cells and which produced none has nowhere else to be read.

Yield is a property of the recording rather than of the sort: a dead stretch of electrode yields no cells however the sorter was parameterized. That is what keeps this figure on the right side of the line this module's docstring draws around sort quality.

Drawn on the channel level's site lattice, and on the same four panels :func:~radiens_qc.plot_quality.plot_site_maps draws, so a site's yield here and its SNR on the channel maps are read off the same cell in the same place. Sites carrying several cells read as a count above one, which is what the sorter's lack of a position of its own otherwise hides.

Amplitude is measured from the waveforms attached to units; with none attached that one panel says "not reported" rather than mapping zeros, and the other three are unaffected.

render

render(specs, sources, options=DEFAULT_RENDER_OPTIONS)

Draw each figure whose source sources holds, in registry order.

A figure whose source is absent is skipped rather than raising, because drawing what a saved report directory supports is a normal mode. The skip is not silent by construction: the caller has the same predicate, so [s for s in specs if not sources.has(s.source)] is what was left out.

options reaches every figure; one with no choice to make ignores it.

Keyed by the spec rather than by name so the level and the caption travel with the figure; {s.name: f for s, f in rendered.items()} is the one-liner when only names are wanted.

report_cache_key

report_cache_key(*, dataset_path, base_name, detection)

Digest identifying the pipeline run a saved report came from.

Adding a field here invalidates every existing key, which is the correct behavior: a key that does not cover something the server acts on is a key that can hand back the wrong report.

resolve_dsp_params

resolve_dsp_params(dataset, dsp_params)

Coerce the mapping form, inject STAGE2, and expand port-less entries.

An entry that names a port 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.

save_figures

save_figures(rendered, out_dir, *, dpi=150)

Write each figure to <out_dir>/<level>/<name>.png and release it.

Cropped to content, since each figure is sized for what it draws rather than laid out on a fixed sheet. Figures are cleared once written: they are built outside pyplot and so are not registered anywhere, but a full run holds every one of them in memory at once otherwise.

save_report

save_report(report, out_dir, *, cache_key=None)

Persist report to <out_dir>/<report.base_name>/.

Data only. Figures are drawn from a report, not stored beside one, so nothing here renders.

Parameters:

Name Type Description Default
report QualityReport

Report to persist.

required
out_dir Path

Parent directory; one subdirectory per recording is created.

required
cache_key str | None

Digest of the pipeline run that produced report, recorded so a later call can tell whether it may reuse this directory instead of recomputing. None writes no key, which reads as a miss.

None

Returns:

Type Description
Path

The report's own directory.

select

select(specs=FIGURES, *, levels=None, names=None)

The figures matching every filter given, in registry order.

None means "no restriction on this", which is not the same as an empty collection -- passing names=[] selects nothing, and does so deliberately.