mne-tools / mne-tools/mne-python

read_ica_eeglab & ica.plot_components: ica.info needs to be filled

Open
#8,581 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

BUG
Dominant language
Python
Stars
3.5k
Forks
1.6k
Avg merge
1d 6h
Merged PRs (30d)
100

Description

(was discussed on gitter as well)
tldr; read_raw_eeglab returns no ica.info, ica.info=raw.info is a bad idea

import mne
raw = mne.io.read_raw_eeglab('sub-009_ses-P3_task-P3_eeg.set')
raw.set_channel_types(mapping={'HEOG_left':'eog','HEOG_right':'eog','VEOG_lower':'eog'})

raw.set_montage('standard_1020',match_case=False)
badChannels = [11 ,27]
raw.info['bads'].extend([raw.ch_names[i] for i in badChannels])  # add a list of channels

ica = mne.preprocessing.read_ica_eeglab('sub-009_ses-P3_task-P3_ica.set')
print(raw)
print(ica)

<RawEEGLAB | sub-009_ses-P3_task-P3_eeg.fdt, 33 x 361472 (353.0 s), ~48 kB, data not loaded>
<ICA | epochs decomposition, fit (imported_eeglab): samples, 28 components>

#ica.set_montage('standard_1020',match_case=False) <-- doesnt work because function does not exist

ica.info = raw.info # <-- alex mentioned we shouldnt do that either.
ica.plot_components(1) # <-- we get an error here because ICA is 28x28 matrix, but raw has shape 30 because the two channels were not yet removed (thus their indices go from 0-29, with 11 and 27 removed, but we cant indice a 28 matrix on position 29))

Results in following error:

---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-4-57fc80c15773> in <module>
      2 
      3 ica.info = raw.info # <-- alex mentioned we shouldnt do that either.
----> 4 ica.plot_components(1) # <-- we get an error here because ICA is 28x28 matrix, but raw has shape 30 because the two channels were not yet removed (thus their indices go from 0-29, with 11 and 27 removed, but we cant indice a 28 matrix on position 29))

/netpool/work/ccs-srv-001/users/ehinger/projects/course_eeg_WS2020/local/venv/lib/python3.8/site-packages/mne/preprocessing/ica.py in plot_components(self, picks, ch_type, res, vmin, vmax, cmap, sensors, colorbar, title, show, outlines, contours, image_interp, inst, plot_std, topomap_args, image_args, psd_args, reject, sphere)
   1802                         image_args=None, psd_args=None, reject='auto',
   1803                         sphere=None):
-> 1804         return plot_ica_components(self, picks=picks, ch_type=ch_type,
   1805                                    res=res, vmin=vmin,
   1806                                    vmax=vmax, cmap=cmap, sensors=sensors,

/netpool/work/ccs-srv-001/users/ehinger/projects/course_eeg_WS2020/local/venv/lib/python3.8/site-packages/mne/viz/topomap.py in plot_ica_components(ica, picks, ch_type, res, vmin, vmax, cmap, sensors, colorbar, title, show, outlines, contours, image_interp, inst, plot_std, topomap_args, image_args, psd_args, reject, sphere)
   1170 
   1171     data = np.atleast_2d(data)
-> 1172     data = data[:, data_picks]
   1173 
   1174     # prepare data for iteration

IndexError: index 28 is out of bounds for axis 1 with size 28

Important note This error can only occur if you copy over raw.info to ica.info (which is empty by default), I have seen this at multiple places.

This is because in order to match raw to ICA-matrices, the "bad" channels are removed, but the original indices are still being used. Therefore we try to indice e.g. data[:,29] which doesnt work because data.shape = 28x28. So I think the underlying reason is, that there is no match of channel names used in the imported ICA against the channel names in header.

Proposed solution:

read_eeglab_ica L2743: add ica.info = info (which was already calculated but never saved to the ICA object.)
and add ica._update_ica_names() somewhere at the end

If I do these two steps (and read in the montage afterwards) my problem is resolved. I can organize a pull-request and unit-test if this is a viable solution.

Intermediate solution:

My problem gets fixed if I manually match the channels (this is effectively the same thing as the proposed solution but outside of ica.py

# assuming ica is subset of raw 
ch_raw = raw.info['ch_names']
ch_ica = ica.ch_names

ix = [k for k,c in enumerate(ch_raw) if c in ch_ica and not c in raw.info['bads']]

info = raw.info.copy()
mne.io.pick.pick_info(info, ix, copy=False)
ica.info = info
ica.info.set_montage('standard_1020',match_case=False) # not strictly necessary I think if raw.info has a montage already
ica._update_ica_names()

ica.plot_components(0)
Minimal reproducible example

bug ipynb html output
data+code, 30mb, ERP core dataset

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in the read_eeglab_ica entry point in ica.py, around the stated line where info is calculated, then inspect ica.plot_components and _update_ica_names. Add the proposed regression unit test for imported EEGLAB ICA data with excluded channels and verify that plotting components no longer indexes beyond the ICA matrix.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
data
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.