Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ICA.plot_overlay not useful for average-referenced EEG data #11805

Closed
drammock opened this issue Jul 14, 2023 · 12 comments · Fixed by #11830
Closed

ICA.plot_overlay not useful for average-referenced EEG data #11805

drammock opened this issue Jul 14, 2023 · 12 comments · Fixed by #11830
Assignees

Comments

@drammock
Copy link
Member

The lower panel of ICA.plot_overlay figures shows a trace of all channels averaged together (before and after ICA). For average-referenced EEG data, the mean of all channels is very very close to zero (on the order of 1e-20 usually) and thus the plot basically shows noise. We should figure out an elegant way to handle this. Possibilities:

  • detect avg ref and then warn?
  • detect avg ref and do something other than take the mean of all channels?
  • do something other than take the mean of all channels in all cases?

I haven't thought much about what is the sensible path here, just documenting this so we don't lose track of it.

Related forum post: https://mne.discourse.group/t/ica-plot-overlay-gives-strange-results/7207/10

cc @mscheltienne who did the legwork figuring out what was going on with the user's plots. 🙏🏻

@mscheltienne
Copy link
Member

Thanks for documenting!
I'll add that we have the usual problem, we don't know what is the EEG reference, even after calling set_eeg_reference.

@larsoner
Copy link
Member

The lower panel of ICA.plot_overlay figures shows a trace of all channels averaged together (before and after ICA)

do something other than take the mean of all channels in all cases?

+1 for this -- I'm a bit surprised it's the average because polarity differences between channels would lead to cancellation even without any processing, making it look like activity is small when it could be huge. Something like the standard deviation/GFP seems much more informative.

@hoechenberger
Copy link
Member

+1 for using RMS for MEG and GFP for EEG

@drammock
Copy link
Member Author

+1 for using RMS for MEG and GFP for EEG

SGTM, @mscheltienne do you want to tackle this one?

@mscheltienne
Copy link
Member

RMS and GFP sounds good. I can add that to my to-do list for next week.

@mscheltienne mscheltienne self-assigned this Jul 17, 2023
@mscheltienne
Copy link
Member

mscheltienne commented Jul 20, 2023

One more point I have a bit of an hard time to wrap my head around, and I'd prefer to figure it out before starting to change the viz functions of the ICA. What about ICA decomposition on combined MEG/EEG (or any combined data types)? I don't have an issue with a fit on the entire data array at once, but the visualization of the mixing matrix seems misleading.

from mne.datasets import sample
from mne.io import read_raw_fif
from mne.preprocessing import ICA


fname = sample.data_path() / "MEG" / "sample" / "sample_audvis_raw.fif"
raw = read_raw_fif(fname, preload=False)
raw.crop(tmin=0, tmax=10).pick(["meg", "eeg"])
raw.load_data()
raw.filter(1., 40.)

ica = ICA(n_components=0.95, method="picard", max_iter=10)
ica.fit(raw)

The code snippet fits 115 components on 204 gradiometers, 102 magnetometers and 60 EEG channels. I can now do ica.plot_components(ch_type="eeg") and get 115 topographies labeled "eeg". Shouldn't I be getting maximum 60 topographies, depending on the PCA step? My point is, shouldn't the mixing matrix be divided by channel types during visualization? From the product of the mixing matrix and to the PCA components data = np.dot(ica.mixing_matrix_.T, ica.pca_components_[:ica.n_components_]) of shape (n_components, n_channels), I can select the channels but I am still left with the full number of components 115. Some of them should have very small weights/eigenvalue and should be disregarded during visualization. Am I missing something?

import numpy as np
from matplotlib import pyplot as plt
from mne.datasets import sample
from mne.io import read_raw_fif
from mne.io.pick import _picks_to_idx
from mne.preprocessing import ICA


fname = sample.data_path() / "MEG" / "sample" / "sample_audvis_raw.fif"
raw = read_raw_fif(fname, preload=False)
raw.crop(tmin=0, tmax=30).pick(["meg", "eeg"])
raw.load_data()
raw.filter(1., 40.)

ica = ICA(n_components=0.95, method="picard")
ica.fit(raw)

picks = _picks_to_idx(ica.info, picks="eeg")
data = np.dot(ica.mixing_matrix_.T, ica.pca_components_[:ica.n_components_])
f, ax = plt.subplots()
img = ax.imshow(data[:, picks], cmap="Reds", vmin=0, vmax=0.1)
ax.set_ylabel("n_components")
ax.set_xlabel("n_channels")
f.colorbar(img)

Screenshot from 2023-07-20 15-41-04

EDIT: Not sure about the independence of sources and how specifically an IC will capture a single channel type.

Other issue related to this point: mne-tools/mne-icalabel#133

@mscheltienne
Copy link
Member

mscheltienne commented Jul 20, 2023

I will add some additional thoughts to my previous comment. If the ICA was fitted on 3 modalites, 204 gradiometers, 102 magnetometers and 60 EEG channels, it is possible to have:

  • a component which contributes to all 3 modalities, e.g. the eye-blink.
  • a component which does not contribute to a modality, e.g. a component which doesn't contribute to the EEG as shown above.

I find misleading to display with plot_components a single modality, ordered by total explained variance instead of explained variance within the modality. One might remove an IC thinking it's an artifact in the plotted modality while it is in fact not contributing to it and was capturing another.

One improvement to the topographic plot of a component within ica.plot_components and ica.plot_properties could be to plot the topographies of all modalities together with the explained variance of the components for each modality.

Anyway, I am sidetracking quite a bit from the original issue, I got a bit lost in the ICA code.. but I think ica.plot_overlay should also be splitting the modalities into different plots.

@hoechenberger
Copy link
Member

hoechenberger commented Jul 20, 2023

I can now do ica.plot_components(ch_type="eeg") and get 115 topographies labeled "eeg". Shouldn't I be getting maximum 60 topographies, depending on the PCA step?

@cbrnr and I ran into a potentially related bug during last week's workshop:
We fit ICA on M/EEG data, ran plot_components(inst=epochs, ch_type="eeg"), and while we were seeing EEG topos, right-clicking on any one of them brought up a properties plot for the respective mag-based data – not eeg, as was expected.

@mscheltienne
Copy link
Member

Seems like the ch_type argument does not even exist for plot_properties. It should be forwarded all the way here:

_plot_ica_topomap(ica, pick, show=False, axes=topo_ax, **topomap_args)

@larsoner
Copy link
Member

@mscheltienne sounds like you are just about ready to open a PR 😄

@mscheltienne
Copy link
Member

@cbrnr and I ran into a potentially related bug during last week's workshop:
We fit ICA on M/EEG data, ran plot_components(inst=epochs, ch_type="eeg"), and while we were seeing EEG topos, right-clicking on any one of them brought up a properties plot for the respective mag-based data – not eeg, as was expected.

Actually, I just notice that lovely note in the documentation:

You can interactively cycle through topographic maps for different channel types by pressing T.

So.. not a bug in the end. But I don't really find this solution satisfactory, it's an hidden feature and doesn't allow the creation of a non-interactive plot with the non-default channel type displayed. I'll make a proposition of re-arranged layout with 1 to 3 topographies.

@hoechenberger
Copy link
Member

I would say it is a bug, if I request to see EEG channels but get MAG instead ... then this is not right.

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 a pull request may close this issue.

4 participants