mne-tools / mne-tools/mne-python
read_ica_eeglab & ica.plot_components: ica.info needs to be filled
Nobody has claimed this yet.
- 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
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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