Metadata-Version: 2.4
Name: radiens-qc
Version: 0.0.2
Summary: radiens-qc turns a Radiens recording into an answerable question: is this data good enough to use?
Author-email: Aaron Kelley <akelley@neuronexus.com>
License-File: LICENSE
Keywords: electrophysiology,neuroscience,quality-control,radiens,signal-quality
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering
Requires-Python: >=3.12
Requires-Dist: matplotlib>=3.9
Requires-Dist: numpy>=1.26
Requires-Dist: pandas>=2.2
Requires-Dist: pydantic>=2.6
Requires-Dist: radiens-core>=0.0.9b2
Description-Content-Type: text/markdown

# radiens-qc

radiens-qc turns a Radiens recording into an answerable question: is this data good enough to use?

Give it a recording and a `VidereClient`. It configures read-time DSP so the KPIs
reflect the spike band, walks the recording in windows, and returns a report:
per-channel SNR / noise floor / event rate, a quality tier per channel, and a
single-letter grade — plus a one-page figure you can hand to a PI.

## Quick start

```python
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())            # "A"
print(report.median("snr"))      # 6.2
print(report.quality_counts())   # {"good": 26, "fair": 4, "poor": 1, "dead": 1}

save_report(report, Path("reports"))   # -> reports/rec01/
```

### Comparing sessions

`save_report` writes a self-describing folder, and the comparative report is
built from those folders — no server, no KPI recompute:

```python
from radiens_qc.compare import ComparativeReport, load_session_summaries, save_comparative_report

summaries = load_session_summaries(Path("reports"))
save_comparative_report(ComparativeReport.from_summaries(summaries), Path("reports"))
```

## Where recordings come from is not this package's problem

`compute_report` takes either a local `Path` or an already-linked
`DatasetMetadata`. Given a path it links the file itself — linking is idempotent
server-side, so passing a path for an already-linked file costs nothing.
Fetching bytes from Drive, S3, or a lab share stays with the caller, which is
what lets the same report code serve any project.

> A bare `str` is deliberately *not* accepted: in radiens-core a string means a
> dataset **id**, not a path. Wrap yours in `Path(...)`.

## What gets written

`save_report(report, out_dir)` creates `<out_dir>/<base_name>/`:

| File | Contents |
| --- | --- |
| `meta.json` | Provenance, thresholds, grade, per-metric summary stats, `schema_version` |
| `channels.csv` | One row per channel: geometry, per-metric values, quality tier |
| `time_series.csv` | Across-channel median per metric per window (human-readable) |
| `matrices.npz` | Full `(n_windows, n_channels)` arrays — what makes a reloaded report re-plottable |
| `<base_name>_quality.png` | The composite page |

`load_report` reads folders written by any version; pre-`schema_version` folders
load with empty `matrices`, which blanks the temporal panels rather than failing.

## Plotting

Plot functions are pure — they build and return matplotlib `Figure` objects and
never touch the filesystem, so figure routing stays yours:

```python
from radiens_qc import plot_report, plot_report_panels

fig = plot_report(report)                 # composite page
panels = plot_report_panels(report)       # {"spatial_snr": Figure, "ranked_bar": Figure, ...}
```

> The composite is laid out as an exact 8.5×11in page. Save it with
> `bbox_inches=None` — matplotlib's `"tight"` default crops the page sizing away.
> `save_report` already does this.

## Installation

```bash
pip install radiens-qc
```

or, with [uv](https://github.com/astral-sh/uv):

```bash
uv add radiens-qc
```

Requires Python 3.12+ and a reachable Radiens server for `compute_report`;
everything downstream of a saved report folder (`load_report`, the plot
functions, `radiens_qc.compare`) works offline.

## License

See [LICENSE](LICENSE).
