Skip to content

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

@behinger

Description

@behinger

(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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions