Releases: neuromechanist/biosigio
biosigIO 1.1.7
biosigIO 1.1.7
Typed read-error hierarchy (biosigio.exceptions)
biosigIO previously raised a generic ValueError for every read failure, so a
caller could not tell why a recording failed to load — a trial-averaged
derivative, a truncated file, and an unsupported format were indistinguishable.
This release adds a typed hierarchy so callers can classify the failure and
surface a specific reason (the NEMAR Zarr viewer uses this to explain why a
recording has no viewer instead of a blank "not available"):
BiosigIOError— base; every error carries a stable.code.UnsupportedFormatError(unsupported_format)FileReadError(file_read_error) — generic fallback for a recognized format
that still couldn't be read.NotContinuousRecordingError(not_continuous) — evoked*-ave.fif/ epoched
*-epo.fifderivatives: valid data, but no continuous time series.CorruptFileError(corrupt_or_truncated) — truncated/non-compliant files,
incomplete CTF.meg4, or an incomplete split chain.EmptyRecordingError(empty_recording)MixedSamplingRateError(mixed_sampling_rate)classify_read_error(exc, filepath)maps a low-level MNE/pyedflib failure to
the right type (shared by the importers).REASONS:code→ default user-facing copy.
The MEG, EDF, BrainVision, and EEGLAB importers now raise these typed errors;
_infer_importer raises UnsupportedFormatError for unknown extensions.
Backward compatibility
BiosigIOError subclasses ValueError, so existing except ValueError /
pytest.raises(ValueError) callers keep working unchanged. The EDF mixed-rate
policy error is now MixedSamplingRateError (still a ValueError, same message).
Tests
Real-file verification (no mocks) for each NEMAR failure mode: a real MNE evoked
-ave.fif → not_continuous, an incomplete split-FIF chain → corrupt, a truncated
CTF .meg4 → corrupt, a truncated EDF → corrupt, plus classifier units and
back-compat assertions.
biosigIO 1.1.6
biosigIO 1.1.6
First release since v1.1.3 (1.1.4 and 1.1.5 were internal version bumps, folded in here).
MEG import: CTF and KIT, alongside FIF
- CTF/VSM
.dsdirectories now import viaread_raw_ctf. - KIT/Yokogawa/RICOH
.con/.sqd/.kdfnow import viaread_raw_kit. Recording.from_filedispatches all three (plus.fif) to the MEG importer.
Mixed-rate EDF/BDF (#737)
EDFImporter/Recording.from_filenow error by default when an EDF/BDF
mixes sampling rates across channels (e.g. PSG: EEG at 200 Hz + SpO2 at
12.5 Hz), instead of failing obscurely.- Opt in with
mixed_rate="resample"to upsample slower channels onto the
fastest channel's grid for a plotting/ML serving copy. Uses
resample_poly(padtype="line")to avoid edge ringing; the original per-channel
rate is recorded in metadata.
Streaming Zarr export
- New
stream_to_zarrconverts arbitrarily large recordings in bounded
memory: lazy MNE read → channel-major float32 memmap → per-channel
resample/quantize/write. Output is byte-equivalent to the in-memory exporter,
so multi-GB recordings convert without changing the result.
Tests and fixtures
- Real vendored fixtures: KIT
.sqdand CTF.ds(catch-alp-good-f,
244 ch incl. simultaneous EEG, 1250 Hz). - Regression tests for FIF + multi-file split-FIF chains, CTF streaming, and the
mixed-rate EDF paths. No mocks.
Fixes
- Preserve importer-loaded events when an
events.tsvis unparsable (noonset
column / missing forced column) instead of wiping them. - Log non-benign MEG
find_eventsfailures rather than swallowing them.
v1.1.3
biosigIO 1.1.3
EEGLAB importer robustness
- Load the standard single-
EEG-struct save form that real EEGLAB writes (loadmatreturns{'EEG': struct}), in addition to the legacy flat layout. Previously a real.setcould import as an empty recording (#100).
Citation and DOI
- Add
CITATION.cff(CFF 1.3.0, structured affiliation with ORCID 0000-0001-5557-259X and the SCCN ROR 01bt2qm76) and.zenodo.json, so this release is archived to Zenodo with a DOI and the repository shows a "Cite this repository" widget..zenodo.jsoncross-links the PyPI project.
Packaging
- Broaden the description and keywords to the full biosignal scope (EEG/EMG/MEG/iEEG; EDF/BDF, EEGLAB, BrainVision, FIF, and the Zarr serving store).
Documentation
- Document the EEGLAB two-save-form robustness and the Zarr store's forward-compatible extended attributes.
Full changelog: v1.1.2...v1.1.3
v1.1.2
EEGLAB importer now unwraps the single EEG struct that scipy.io.loadmat returns for real .set files, so they load with signals/channels instead of silently producing an empty Recording.
Robust to both the nested and flat save forms. Unblocks the NEMAR Zarr serving-copy pipeline.
v1.1.1
biosigio 1.1.1
Bound peak memory for large-file Zarr conversion so multi-GB recordings fit a free 16 GB ubuntu-latest runner. No API or behavior change. Closes #95.
Fixed
- Zarr exporter streaming — per
(modality, rate)group the exporter no longer builds a float64 list of every channel plus a float64vstackcopy plus the int16 copy at once (~4.5x the output). It now preallocates the output-dtype array and resamples + quantizes one channel at a time straight into it, dropping the float64 double-buffer. Round-trip output is bit-identical. - EEGLAB one-shot frame build — the signal DataFrame is built in a single allocation (samples x channels) instead of one column at a time, avoiding the O(n_channels) block-manager reallocation that roughly doubled peak RAM for high-channel-count
.fdtrecordings; the.fdt's native float32 is preserved (no silent upcast).
Measured: a 5 GB EEGLAB .set+.fdt (346ch x 3.6M @ 500 Hz) now converts at ~10 GB peak RSS (load 5.5 GB + export), down from a fragmented multi-GB path; round-trip events and rate-group splitting unchanged. New test_zarr_memory.py guards the export's marginal peak via a subprocess ru_maxrss check.
v1.1.0
biosigio 1.1.0
Two importer features, found while validating real BIDS -> Zarr conversion.
Added
- EEGLAB
.fdtsupport —.setfiles whose signal matrix lives in a separate float32.fdt(EEGLAB's default for large recordings, whereEEG.dataholds the.fdtfilename) now load. The sibling.fdtis resolved by the.setpath, so BIDS-renamed files load even thoughEEG.datakeeps the original.fdtname. Unblocks multi-GB.setrecordings. bids.apply_events_tsv— load the authoritative BIDS_events.tsvintorec.events(onset, duration withn/a->0.0, description fromtrial_typethenvalueper row), the symmetric counterpart ofapply_channels_tsv. Carries events through to EDF+/Parquet/Arrow/Zarr export for files whose native markers are empty.
No public API or behavior change to existing paths. Closes #94.
v1.0.3
biosigio 1.0.3
Internal naming cleanup: the legacy emg local variable (a leftover from the EMG-only era, retired as the EMG class alias was removed in 1.0.0) is renamed to rec throughout the package, examples, and docs so variable names match the modality-agnostic Recording API. Domain-descriptive names that genuinely denote EMG data (emg_only, emg_channels, emg_subset, emg_data, subset_emg, bicep_emg) are preserved.
Changed
- Renamed the
emg/new_emglocals torec/new_recacross importers, exporters, visualization, and theRecordingcore (945 replacements over 74 files). - Renamed the MNE helper
raw_to_emg→raw_to_recording(definition + MEG/BrainVision callers). - Renamed visualization parameters
emg_object/emg_original/emg_reloaded→rec_object/rec_original/rec_reloaded. - Updated README, all docs examples, and the API reference to the
recconvention.
No public API or behavior change: the Recording class, channel_type="EMG" modality, channel labels, and all method signatures are unchanged. Closes #89.
1.0.2
EEGLAB events fix. No breaking changes.
Fixed
- EEGLAB
.setevents now load into the standardRecording.eventsDataFrame (onset/duration in seconds, description = the EEGLAB eventtype), converting the 1-based sample latency via(latency - 1) / srate, instead of being stashed as a list of dicts inmetadata['events']. This makes EEGLAB consistent with the EDF+/BDF+ and WFDB importers, so EEGLAB events are now carried by the EDF/Parquet/Arrow/Zarr exporters. A present-but-zerosratenow falls back to 1000 Hz.
Internal
- The EEGLAB importer's local
emgvariable was renamed torec(the broaderemg→reccleanup is tracked in #89).
1.0.1
Documentation accuracy pass + small Recording convenience accessors. No breaking changes.
Added
Recording.get_n_channels(),get_n_samples(),get_sampling_frequency()(raises on mixed per-channel rates),get_duration()(total window length in seconds),has_metadata(key).
Docs
- Complete docs-vs-code revision (driven by an in-house multi-agent audit + a Codex pass): removed examples using nonexistent methods/kwargs, corrected stale claims (EDF+/BDF+ annotations load into
eventsand embed on export; EDF/BDF export rejects mixed sampling rates; OTB/EEGLAB/XDF specifics; auto-detect extensions; Trigno structure;_channels.tsvnaming; optional extras; version), and added API pages for the CLI, modality vocabulary, BIDS helpers, serialization schema, and signal-analysis module.
CI
- Workflows path-filtered: docs-only changes skip the Python test matrix, and code-only changes skip the docs build.
1.0.0
biosigIO 1.0.0 — the package is renamed emgio -> biosigio and is now the multimodal biosignal I/O library (EEG, EMG, iEEG, MEG, and behavioral/marker streams).
Breaking changes
- Package renamed:
import emgio->import biosigio(pip install biosigio). Theemgioproject on PyPI is frozen at 0.6.0; a transitionalemgioshim release will follow. EMGalias removed: useRecording(it was a deprecated alias since 0.5.0).
Highlights since 0.x
- Importers: EEGLAB, Trigno, OTB, EDF/BDF(+), WFDB, XDF, MEG (.fif/CTF) and BrainVision via MNE, and proprietary electrophysiology (Intan/Blackrock/Spike2/Plexon/Micromed/Neuralynx) via python-neo.
- Exporters: EDF/BDF(+) with auto format selection, Parquet + Arrow/Feather (lossless), and a Zarr serving store (viewing + inference + training from one derived store).
- Modality-agnostic
Recordingclass, anti-aliasedresample, blockingtytype-check gate, and a full docs site.
Install
pip install biosigio # core
pip install 'biosigio[all]' # + meg, arrow, neo, zarr, dev, docs