What’s new#

Unreleased#

Nothing yet.

0.2.0#

A release that fixes the problems found in a full review of 0.1.0. Many of them changed results silently, so results differ from 0.1.0, in particular sampling rates and event times of XDF recordings with dropouts, bad channels, ICA and the selected components. Re-run analyses made with 0.1.0.

Behaviour changes#

  • XDF timing. XDFLoader uses the EEG stream’s nominal sampling rate. Dropouts are filled with interpolated samples and annotated BAD_eeg_missing, and markers are placed by sample index, so events stay on the right samples after a dropout or with clock drift. Before, a single dropout lowered the sampling rate (a 2 s gap in a 10 s, 500 Hz recording gave 400 Hz). The new MultimodalRecording.eeg_timestamps maps Tier-2 and event timestamps onto Raw times. Voltage units of auxiliary streams are now converted to volts too (a microvolt ECG was stored 1e6 too large).

  • AMICA sample rejection is on by default when ICA runs with jamica ({"do_reject": True, "rejsig": 3.0, "numrej": 10}), as in the MATLAB pipeline. Use jamica_kwargs={"fit_params": {"do_reject": False}} for the old behaviour. The number of rejected samples is recorded in the provenance (amica_rejected_samples).

  • Bad channels. get_bad_chs() works on a copy high-passed at 1 Hz (new highpass parameter), finds flat channels before the average reference and runs the line-noise criterion before the notch filter. It warns when more than max_bad_fraction (default 0.2) of the EEG channels is flagged.

  • Final reference. EEGPreprocessor interpolates bad channels first and then average-references all EEG channels, as in BeMoBIL, so the cleaned EEG sums to zero at every sample.

  • ICLabel selection. In probability-threshold mode (0 <= thresh <= 1) an IC is removed only when its winning class is one to remove and its probability reaches thresh; uncertain ICs are kept. Before, every IC below the threshold was removed. compute_ica() now uses the pipeline’s defaults: popularity vote, keep all classes except "eye blink", and a 1.75 Hz high-pass. Unknown class names raise.

  • Epoching. EpochPreparer no longer shifts events by 60 ms by default (tshift=0); set the delay of your own setup.

  • Motion. process_rigid_body() uses BeMoBIL’s low-pass defaults (6 Hz, and 18 Hz after differentiation), bridges motion capture dropouts before filtering, takes angle_unit="deg" for Euler angles in degrees, and computes acceleration without velocity.

  • Dipoles. trans="fit" fits a rigid transform (it estimated a head scale that was then ignored). A warning is shown when electrode positions have no fiducials.

  • Reproducibility. Without rng_seed, each run draws a seed and records it in the provenance, together with the ICA method actually used, its parameters and the epochs FASTER dropped.

  • Optional dependencies no longer fail silently: missing jamica gives a visible warning, and missing mne-denoise or meegkit raises.

  • overwrite=True removes all derivatives of an earlier run first, and overwrite=False fails before processing instead of after it. Report figures are saved in a *_figures/ folder.

Removed and renamed#

  • XDFLoader(on_mismatch="crop") was removed: with many streams and frequent dropouts, cropping to the overlap of all streams discards usable EEG. "pad" is the only option.

  • plot_ERSP was removed; use plot_TFR().

  • The misspelled EpochPreparer(epoch_tlimes=...) is now epoch_tlims.

  • pybemobil.viz.utils and the private names in pybemobil.preproc’s __all__ were removed. The environment variable for the report’s parallel jobs is now PYBEMOBIL_REPORT_N_JOBS (was BPN_REPORT_N_JOBS).

Bug fixes#

  • Text from the data (marker names, channel names, labels) is escaped in the HTML report.

  • plot_various_ERPs() works with a subtraction event and other events; plot_PSD() shows confidence bands across epochs for a single object and accepts Spectrum baselines; plot_ERP() draws baseline and ROI spans on every axis; plot_TFR() works on ICA sources without picks.

  • get_bad_chs() works at sampling rates of about 100 Hz and names channels with NaN values.

  • make_bids_dataset_description() leaves unset fields out, and batch_export_to_bids() warns about failed runs (on_error="raise" stops at the first one).

  • NumpyEncoder handles numpy integers and bools; the report’s 3-D co-registration plot is no longer skipped on macOS; fsaverage is only downloaded when needed, and SUBJECTS_DIR is no longer set.

  • XDFLoader options are validated, so typos raise instead of silently falling back to defaults.

Other changes#

  • Lower memory use. A pipeline run with the report needed up to 12 times the size of the recording in memory; now it needs about 10 times (measured on a 10 min recording), and the saving grows with the length of the recording. Bad-channel detection copies only the EEG channels, and the ICA section of the report is built from at most 300 one-second epochs spread over the recording instead of all of it, so its plots show a sample of the data.

  • Dependencies: scipy and Pillow are declared, mne-denoise>=0.0.3 is required, and seaborn is the optional report extra (pandas is no longer required).

  • The comparison with the MATLAB pipeline was corrected (bad-channel parameters, filtering, output files and a working minimal example).

  • Documentation that disagreed with the code was corrected, the Zenodo DOI is in CITATION.cff and the README, and the examples write their output to the working directory.

  • CI tests on macOS too, and releases are only published when the tests pass.

0.1.0#

First public release of pybemobil, a Python implementation of the MATLAB BeMoBIL pipeline built on MNE-Python.

Highlights#

  • XDFLoader loads multimodal XDF recordings and aligns auxiliary streams (ECG, gaze, EMG, EDA, motion) to a common time grid.

  • EEGPreprocessor runs the full EEG pipeline (ZapLine-plus, bad channel detection, filtering, optional ASR, ICA with ICLabel, dipole fitting) with provenance tracking and an HTML report.

  • export_to_bids() exports datasets to BIDS.

  • Rigid body motion processing, epoching helpers and plotting utilities.

  • The MoBI 128-channel montage ships with the package in pybemobil/data/montages.

Changes for users of development versions#

If you used a pre-release snapshot (previously named bemobil-mne), note:

  • The package is renamed: pip install pybemobil and import pybemobil replace bemobil-mne / bemobil_mne. Derivatives are now written to derivatives/pybemobil/.

  • EEGPreprocessor.run() keeps the montage set by the loader instead of always applying the 10-05 template.

  • get_bad_chs(notch_lines=None) now skips notch filtering. The default is still "europe".

  • metadata["raw_source"] is the caller’s own object.

  • PSD plots use density normalization (normalization="length"), so values are scaled by the sampling frequency compared to before.

  • Euler angles from rigid body processing are unwrapped by default (unwrap=True).

  • Montage files moved to pybemobil/data/montages and are now included in the wheel.

  • The default 10-05 template is colin27_1005 on MNE versions where standard_1005 was removed.

  • Requires Python 3.11 or newer and MNE-Python 1.13 or newer.