Skip to content

FIX: Fix montage handling for EOG channels in EEGLAB reader - #14396

Merged
larsoner merged 4 commits into
mne-tools:mainfrom
sappelhoff:fix-10505-eeglab-eog-montage
Oct 12, 2026
Merged

larsoner merged 4 commits into
mne-tools:mainfrom
sappelhoff:fix-10505-eeglab-eog-montage

Conversation

@sappelhoff

Copy link
Copy Markdown
Member

Gemini 3.8 flash helped with writing the code for this one

Summary

When reading EEGLAB files with channel positions and EOG channels:

  • _get_montage_information() only adds channels whose type is in ("eeg", "seeg", "dbs", "ecog") to DigMontage.
  • _get_info() prunes any channels designated via the eog parameter from eeg_montage.
  • _set_dig_montage_in_init() calls self.set_montage(montage, on_missing="ignore") instead of padding missing channels with dummy NaN coordinates.

@sappelhoff
sappelhoff force-pushed the fix-10505-eeglab-eog-montage branch from d4584a1 to e6e9833 Compare October 9, 2026 19:49
Comment thread doc/changes/dev/14396.bugfix.rst Outdated
@@ -0,0 +1 @@
Fix bug where :func:`mne.io.read_raw_eeglab` and :func:`mne.io.read_epochs_eeglab` raised a false warning when setting montage with EOG channels, by `Stefan Appelhoff`_.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Fix bug where :func:`mne.io.read_raw_eeglab` and :func:`mne.io.read_epochs_eeglab` raised a false warning when setting montage with EOG channels, by `Stefan Appelhoff`_.
Fix bug where :func:`mne.io.read_raw_eeglab` and :func:`mne.read_epochs_eeglab` raised a false warning when setting montage with EOG channels, by `Stefan Appelhoff`_.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 48e0e7b.

Comment thread mne/io/eeglab/eeglab.py Outdated
)
_ensure_fiducials_head(eeg_montage.dig)
else:
eeg_montage = None

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any chance to DRY the raw and epochs code? A quick look suggests they are very similar

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RawEEGLAB and EpochsEEGLAB already share _get_info() and _set_dig_montage_in_init() (their __init__ methods differ because BaseRaw and BaseEpochs have separate initialization paths for streaming vs. 3D trial arrays).

However:

  1. In eeglab.py, the montage was previously being constructed twice (created once in _get_montage_information(), then deconstructed and recreated in _get_info()). I passed eog directly to _get_montage_information() so channel types are set up-front, building the montage only once and eliminating the duplicate reconstruction block in _get_info().
  2. In test_eeglab.py, I parametrized test_eeglab_eog_montage() across both read_raw_eeglab and read_epochs_eeglab, DRYing the test logic.

When reading EEGLAB files, channels of non-EEG types (such as EOG, ECG, STIM, or MISC) should not be included in the DigMontage. In MNE-Python, inst.set_montage() only applies positions to EEG-like channels and warns when non-EEG channels are present in montage.ch_pos, recommending to omit them when creating the montage.

This commit:

- Updates _get_montage_information() to take the eog parameter and designate EOG channels in ch_types before filtering montage_ch_pos, ensuring the montage is created only once without non-EEG channels.

- Updates ch_types in _get_info() for channels matching the eog parameter.

- Replaces manual NaN padding of missing channels in _set_dig_montage_in_init() with self.set_montage(montage, on_missing='ignore'), avoiding dummy NaN points in DigMontage.

Fixes mne-tools#10505
Add parametrized test_eeglab_eog_montage() to verify that:

1. read_raw_eeglab() with eog parameter on files with channel positions does not raise RuntimeWarning regarding setting positions of EOG channels, correctly sets channel types to 'eog', and omits EOG channels from the returned montage.

2. read_epochs_eeglab() with eog parameter behaves similarly without warnings.

Also allow **kwargs in get_bad_information() in test_fidsposition_information() to accept additional keyword arguments.

References mne-tools#10505
Add towncrier changelog fragment documenting the bugfix for mne-tools#10505.
@sappelhoff
sappelhoff force-pushed the fix-10505-eeglab-eog-montage branch from e6e9833 to 48e0e7b Compare October 10, 2026 17:50
@sappelhoff
sappelhoff requested a review from larsoner October 10, 2026 18:50
Comment thread mne/io/eeglab/tests/test_eeglab.py Outdated
Comment on lines +972 to +975
with warnings.catch_warnings():
warnings.filterwarnings("error", message=".*setting position.*")
warnings.filterwarnings("ignore", message=ignore_warn)
inst = reader(fname, eog=eog)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You shouldn't need to error filter as it's our default in tests. So I think this can more simply be a single

with pytest.warns(RuntimeWarning, match="setting position")

or similar

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, I pruned the unneeded bits

@larsoner
larsoner merged commit c42e726 into mne-tools:main Oct 12, 2026
25 checks passed
@larsoner

Copy link
Copy Markdown
Member

Thanks @sappelhoff !

@sappelhoff
sappelhoff deleted the fix-10505-eeglab-eog-montage branch October 12, 2026 20:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

read_raw_eeglab fails to take eog param into account when setting montage

2 participants