Comparison with the BeMoBIL pipeline#
The BeMoBIL pipeline (Klug et al., 2022) is a widely-used MATLAB toolbox for automated MoBI data analysis. PyBeMoBIL is its Python/MNE counterpart, designed to produce equivalent results while integrating with the broader scientific Python ecosystem.
This page describes what the two pipelines share, where they differ and why, and shows a side-by-side minimal example.
Overview#
MATLAB BeMoBIL pipeline |
PyBeMoBIL |
|
|---|---|---|
Language |
MATLAB |
Python |
EEG framework |
EEGLAB |
MNE-Python |
ICA |
AMICA (native MATLAB) |
AMICA via |
IC classification |
ICLabel (EEGLAB plugin) |
|
Line-noise removal |
ZapLine-Plus (auto-detect) |
ZapLine-Plus via |
Bad channel detection |
|
PyPREP + FASTER + optional line-noise criterion |
Outlier rejection for ICA |
AMICA sample rejection ( |
AMICA sample rejection via |
BIDS support |
Manual / separate toolbox |
Built-in via |
Provenance |
|
JSON descriptor in |
Output format |
|
FIF ( |
Intentional differences#
- Bad channel detection method
The original MATLAB implementation detects bad channels with
clean_rawdata’s channel criteria on average-referenced data: a channel is flagged if it correlates belowchancorr_crit=0.8with its robust estimate from the other channels for more thanchan_max_broken_time=0.5of the recording (flatline and line-noise criteria are off). Because this estimate is randomized, detection is repeatedchan_detect_num_iter=20times and a channel is rejected if it was flagged in at leastchan_detected_fraction_threshold=0.5of the runs.num_chan_rej_max_target=1/5is a soft target: if more than a fifth of the channels pass that threshold, the threshold is raised so that roughly a fifth are rejected (ties can still add more).PyBeMoBIL uses PyPREP (NaN/flat, deviation, high-frequency noise, correlation and, when a montage is set, RANSAC) combined with FASTER’s channel correlation and variance criteria, plus an optional line-noise criterion (see
get_bad_chs()), on a 1 Hz high-passed, average-referenced copy of the data (flat channels are found before referencing). It runs once and has no iteration. Instead of raising the threshold likenum_chan_rej_max_target, it warns when more than a fifth of the channels is flagged (max_bad_fraction=0.2). Both approaches flag channels that stop correlating with their neighbours, but the exact channels detected can differ.- ASR
The original MATLAB implementation does not apply ASR; it relies on AMICA’s built-in sample rejection (
AMICA_autoreject=1, 3 SD, 10 rejection rounds). PyBeMoBIL does the same: ASR is off by default (asr=False), and when ICA runs withjamica(the default), AMICA’s sample rejection is on with the same settings. Passjamica_kwargs={"fit_params": {"do_reject": False}}to turn it off. ASR is available as an opt-in (asr=Trueorasr={"cutoff": 20}), e.g. when ICA falls back to picard, which has no sample rejection.- Filtering
The original MATLAB implementation does not filter the continuous data before ICA. Only the copy used to fit AMICA is high-passed at 1.75 Hz (FIR, order 1650), and after the ICA solution is copied back, a final 0.2 Hz high-pass is applied (
final_filter_lower_edge=0.2).PyBeMoBIL band-passes the continuous data at 0.1-100 Hz after bad channel detection and before ICA (
filter_bands=(0.1, 100.0)), and this filter stays in the cleaned output. The ICA copy is high-passed at 1.75 Hz as in MATLAB (filter_bands_ica=(1.75, None)), but with MNE’s default FIR design, whose filter length is chosen automatically rather than fixed at order 1650. There is no final high-pass after ICA. To get closer to the MATLAB behaviour, skip the main bandpass and add the final filter yourself:preprocessor = EEGPreprocessor(loader=loader, filter_bands=(None, None)) raw_clean, report, metadata = preprocessor.run(fname_in, fname_out=fname_out) raw_clean.filter(l_freq=0.2, h_freq=None)
- Output format
The original MATLAB implementation saves
.set/.fdt(EEGLAB) files. PyBeMoBIL saves MNE’s FIF format and organizes outputs as BIDS.fname_outmust be a BIDS-style path inside aderivativesfolder (e.g.bids/derivatives/pybemobil/sub-01/eeg/sub-01_task-walk_eeg.fif); its entities name every output. The FIF files use the extension offname_out:.fifby default, or.fif.gziffname_outends in it. Written next tofname_out(seeget_derivative_paths()):*_desc-clean_eeg.fif: the cleaned recording*_desc-preica_eeg.fif: the data ICA was applied to (for re-applying ICA)*_ica.fif,*_iclabels.json,*_badchannels.json*_dipoles/(one.bdipfile per IC) and*_trans.fif, when fitting dipoles*_report.html, and*_figures/for figures saved with the report (e.g. the joint ERP plots)
By default (
save_bids_raw=True) the untouched input is also written once to the raw part of the BIDS dataset withmne_bids.write_raw_bids(). The BIDS root is taken from the folder abovederivativesunlessbids_rootis given.- Bilateral dipole fitting (not yet available)
The original MATLAB implementation supports
number_of_dipoles=2, fitting a symmetric bilateral dipole pair per IC. This is not currently implemented in PyBeMoBIL:fit_dipoles_on_ica()’sn_dipolesparameter only supports1(a single equivalent current dipole viamne.fit_dipole()); passingn_dipoles=2raisesNotImplementedErrorrather than silently returning a single-dipole result. MNE has no built-in constrained two-dipole fit, so bilateral fitting would require a custom optimization - unlike the other differences on this page, this is a genuine feature gap rather than a deliberate design choice, and is open for future contribution.
Minimal example#
The following script replicates the core BeMoBIL EEG preprocessing steps using PyBeMoBIL.
from pybemobil.io import XDFLoader
from pybemobil.preproc import EEGPreprocessor
# --- Equivalent to bemobil_process_all_EEG_preprocessing ---
loader = XDFLoader(
montage="easycap-M1", # channel_locations_filename
target_sfreq=250.0, # resample_freq = 250
old_reference="FCz", # example; MATLAB's default ref_channel = [] keeps
# the recording reference as is
)
preprocessor = EEGPreprocessor(
loader=loader,
line_noise_freq="europe", # 50 Hz; use "usa" for 60 Hz
line_noise_method="adaptive", # zaplineConfig.noisefreqs = []
filter_bands=(0.1, 100.0), # PyBeMoBIL only; MATLAB does not filter here
filter_bands_ica=(1.75, None), # filter_lowCutoffFreqAMICA = 1.75
downsample_ica=250.0, # resample_freq = 250
ica_method="jamica", # AMICA (via jamica), sample rejection on
# (AMICA_autoreject = 1, 3 SD, 10 rounds)
thresh=-1, # iclabel_threshold = -1
# include_labels default = all except eye blink (iclabel_classes = [1 2 4 5 6 7])
fit_dipoles=True, # dipole fitting on
rv_thresh=None, # residualVariance_threshold = 100 (no threshold)
remove_outside_head=False, # do_remove_outside_head = 'off'
)
# fname_out is a BIDS-style path inside derivatives/; the BIDS root is "bids"
raw_clean, report, metadata = preprocessor.run(
"sub-01_task-walk_run-01.xdf",
fname_out="bids/derivatives/pybemobil/sub-01/eeg/sub-01_task-walk_run-01_eeg.fif",
overwrite=True,
)
# Access pipeline outputs
ica = metadata["ica"]
ic_labels = metadata["ic_labels"]
dipoles = metadata["dipoles"]
bad_ch_dict = metadata["bad_ch_dict"]
Reference#
Klug, M., Jeung, S., Wunderlich, A., Gehrke, L., Protzak, J., Djebbara, Z., Argubi-Wollesen, A., Wollesen, B., & Gramann, K. (2022). The BeMoBIL Pipeline for automated analyses of multimodal mobile brain and body imaging data. bioRxiv. https://doi.org/10.1101/2022.09.29.510051