ICA failing to exclude bad channels

Dear MNE users,

I'm trying to analyse some EEG data which contains a few very noisy channels (amplitude is often to the order of 1V). This seems to be causing problems with ICA, even after bad channels are excluded

I begin EEG preprocessing by excluding the bad channels, and then re-referencing the data to the average of all remaining channels with:

raw.info['bads'] = ['CP1',etc...]
raw, ref_data = set_eeg_reference(raw, ref_channels=None, copy=False)

After filtering and trial-by-trial artifact rejection, I run ICA with:

ica = mne.preprocessing.ICA(n_components=.99, method='fastica',
                            max_iter=500, random_state=ica_random_state)
picks = mne.pick_types(epochs.info, meg=False,
                       eeg=True, eog=False, stim=False, exclude='bads')
ica.fit(epochs)

This usually outputs a single IC component, and does nothing to address blinks/saccades etc that are clearly present in the raw data. My feeling is that ICA is somehow failing to exclude the bad channels, meaning that (in the presence of much higher variance, introduced by the noisy channels) it is relatively blind to 'normal' artifacts in the EEG.

Does anyone know why this might be happening? Any advice on the problem would be greatly appreciated!

Regards
Lyam

Dear Lyam,

ICA does ignore the bad channels see for example:

https://github.com/mne-tools/mne-python/blob/master/mne/preprocessing/ica.py#L408

can you share a full gist of code to replicate the problem?

Alex

I suspect the problem may be in the definition of 'bads' -- Lyam are
you explicitly marking channels 'bad' or you expecting automatic
detection of bad channels (as e.g. some EEGLAB functions do)?

Phillip

Dear all,

Thanks for the responses. To clarify, I'm excluding bad channels based on visual inspection of the raw data, and defining them manually with raw.info['bads'] = []. If it helps, I can confirm that MNE is registering this step, as print raw.info['bads'] lists all the specified channels, as one would expect.

Regards

Lyam

Dear Lyam,

it's always possible there is a bug. Please share a script and maybe data if
it's not on sample data so we can replicate and investigate.

Alex

Hi all

Just to provide a bit more background on behalf of Lyam - we definitely
have the noisy channels explicitly listed in raw.info['bads'] and told ICA
to ignore these. However, the single IC we get weights entirely on the
period of the experiment when the bad channels were going crazy (which was
one period in the middle of the experiment).

Some additional background that might be helpful: We're using an ANT 72
channel amp (same as the BrainProducts QuickAmp), which records the data
relative to the average reference. An unfortunate consequence of that is
that if any channels are particularly noisy (as they were here, due to bad
electrodes), the noise gets introduced into all the other channels. So, as
Lyam said, immediately after import we mark the bad channels as "bads" and
then re-reference to the average of the remaining channels. Visually, this
is quite effective at removing the artifacts - during the period where the
artifacts were present, we can now see the EEG clearly and subjectively, it
doesn't look any noisier or otherwise different from other periods of the
experiment. I suppose it's possible that ICA is sensitive to residual crap
in that section of the EEG that isn't obvious to the naked eye; however
based on visual inspection I find it very difficult to believe that 99% of
the variance in the entire dataset is attributable to such hypothetical,
non-obvious residual noise.

Anyway, Lyam will share the data and code with you, Alex, and hopefully you
can help us out! This is not an isolated case - quite a few data sets were
acquired in three different experiments before we replaced the bad
electrodes, so we are highly motivated to find a solution!

Thanks in advance,
Aaron

Dear MNE users,

Just a reminder - we were using the following lines of code to exclude bad channels:

raw.info['bads'] = ['CP1',etc...]
raw, ref_data = set_eeg_reference(raw, ref_channels=None,copy=False)

It turns out that set_eeg_reference was not applying an average reference, which caused our problem. This is fixed by adding raw.apply_proj() immediately after the set_eeg_reference line.

Kind regards

Lyam

Hi,

Did you try:

raw, ref_data = set_eeg_reference(raw, ref_channels=[],copy=False)

As far as I remember ref_channels=None just does nothing. Passing an empty
list should trigger average referencing.

Best,
Denis

Hi Denis,

Thanks for your reply. Apologies if I was unclear in my last email - the problem has actually been resolved. I was simply posting this in order to close the thread and provide help for anyone who might have the same problem in the future :slight_smile:

Regards

Lyam.

Thank you Lyam

This is helpful and appreciated. I think this API is a bit too subtle to
endure. We will hopefully have a clearer and less silent referencing API.

Denis

Hi Dennis

This is the text on the MNE API page (v 0.14 stable) that we were working
off of:

No re-referencing:If the EEG data is already using the proper reference, set
ref_channels=[]. This will prevent MNE-Python from automatically
re-referencing the data to an average reference.Average reference:A new
virtual reference electrode is created by averaging the current EEG signal.
Make sure that all bad EEG channels are properly marked and set
ref_channels=None.
In this case, we wanted to re-calculate the average reference after
removing the noisy channels. From my reading, [] will have no effect on the
reference, whereas "None" (somewhat counterintuitively) specifies that we
want the average reference.

The issue was that I stopped reading there, and didn't see the box below
that says:
*Note*
*In case of average reference (ref_channels=None), the reference is added
as an SSP projector and it is not applied automatically. For it to take
effect, apply with method apply_proj. For custom reference (ref_channel is
not None), this method operates in place.*

I will say this is pretty counterintuitive and inconsistent: depending on
what reference you choose you may or may not have to subsequently run
apply_proj. I agree that a more robust/consistent/intuitive API would be
appreciated and likely help prevent errors in people's workflows.

thanks
Aaron