Skip to content

radiens_qc.figures

Every figure the package draws, named once, so a caller asks for them by level or name.

The plotting modules each own one kind of drawing and know nothing about each other. This is the one place that knows they exist together: what each figure is called, which level of the data it shows, and which of the package's four objects it takes.

A caller wanting everything says so once::

specs = FIGURES
sources = fetch_figure_sources(vc, dataset, specs, report=report)
save_figures(render(specs, sources), Path("figures"))

Adding a figure is one row here. Nothing else changes -- not an __all__, not a caller -- which is the point of a registry over a hand-written list at every call site.

The division of labour this keeps: a figure decides how to draw, the registry decides what exists, and the caller decides what to render and where to put it.

Classes

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.

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.

Functions:

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

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.

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.

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.