Check for ICA (toward Epochs Rejection)

thanks Richard,

I removed now the “reject = dict(mag=5e-12, grad=4000e-13)” from ICA. I had this doubt too, but I noted that “keeping it” versus “removing it” produced a lower percentage of epochs rejected (throught the “autoreject” method) as showed by my results in the first post of this thread, for instance:
ICA, CAR = 2.2% epochs rejected (with the “reject” parameter in the ICA enabled)
ICA, CAR = 61% epochs rejected (with the “reject” parameter in the ICA disable)

I removed “decim” as well now. I read from the documentation that it helped to speed up the process, but I’ve just tested now the ICA without it and I could not note any reduction in speed. So I’m totally agree with you that it is not necessary, at least in my case.

I’ve updated the baseline values according to the documentation as follows: baseline=(-0.5, -0.2))

According to what you said, after the ica.fit(epochs) I manually inspect the components as follows:

ica.plot_components()
ica.plot_sources(epochs)

After the above stage, I already can see which component is associated to the ocular activity, and I could therefore easily remove it using e.g. ica.exclude = [0].
I already test it, and it well cleans the ocular activity from epochs.
So I’m wondering why I should use also the following lines:

eog_epochs = mne.preprocessing.create_eog_epochs(raw_filtered, ch_name="Fp1",  baseline=(-0.5, -0.2))
eog_indices, eog_scores = ica.find_bads_eog(eog_epochs, ch_name="Fp1")
ica.exclude = eog_indices

Looking at the documentation , the approach associated to the above 3 lines looks great in case you want “automatically mark for exclusion any ICs that match the EOG/ECG pattern”.
On the other hand “you must hope that the frontal EEG channels only reflect EOG and not brain dynamics in the prefrontal cortex (or you must not care about those prefrontal signals)”… and in my case I’m focusing exactly only in frontal brain activity (I’ve only 7 frontal channels).

In general I would love to use “ica.find_bads_eog” for the following automatic exclusion of the components, but my test showed that “MNE didn’t manage to extract an IC related to ocular activity” using “Fp1”. Afterwards, if the only solution would be playing with the “threshold” parameter (I did it and I got the component [0] setting the threshold to 1.7) , but my main concern is that the analysis will turn manual again, so why not simply inspect and remove the components manually without using the above 3 lines of code?

However, since I guess would be great for anybody to integrate an automatic approach as well, I’m wondering if could be possibile to use the approach described below order:

  1. run the ica.fit on epochs
  2. get the eog epochs (e.g. eog_epochs = mne.preprocessing.create_eog_epochs(raw_filtered, ch_name=“Fp1”, baseline=(-0.5, -0.2)))
  3. get the eog_indices (e.g. eog_indices, eog_scores = ica.find_bads_eog(eog_epochs, ch_name=“Fp1”)
  4. At this stage, if the eog indices list is empty, I could implement a code in order to automatically lower the “threshold” of “ica.find_bads_eog” till the script won’t return at least one ocular component.
  5. the default threshold is set to 3.0. If the code found at least one ocular component with the threshold set between 1.5 and 3.00 (decreasing the threshold value from 3.00 in step of 0.10) then the ocular component is removed automatically. Otherwise, If the threshold is lowered by the script to a value lower than 1.5, than the code require the manual inspection ( visually select and manually remove the component)

What do you think about the above approach?
Off course “1.5” threshold limit for the manual inspection is just a sample value. Could you let me know a value that “make sense”, to replace to "“1.5”? I don’t know if it is too low (I checked the documentation but I could not find any details about that).

Thanks for keeping this forum so stimulating!

This seems like a bug to me, honestly… you don’t have MEG data, so MEG reject parameters shouldn’t do anything. You should consider reporting this to the autoreject project.

You don’t need to use this if you don’t want to perform completely automated artifact removal.

You have the exact same issue when manually removing ICs. You have so few electrodes and they’re all contaminated by ocular activity, so I would assume you’re always at a high risk of accidentally removing brain signal. That’s why it’s extremely important to visually inspect both the ICs and the results of data cleaning.

An alternative could be to simply drop all epochs containing blinks, and skipping ICA altogether.

I would advise against this as the first step. You should try to find a single value that works for your study, and stick with it. Otherwise you will end up removing ICs even if none related to the EOG could be reliably identified.

No, you will have to explore this yourself. It may be different with every dataset, so I cannot give general advice.

thank you @richard
Regarding the epoch rejection, after the ica.apply I tried to use a method I found in MNE documentation:

# Reject epochs (MNE method)
reject = dict(eeg=40e-6)  # unit: V (EEG channels)
epochs.drop_bad(reject=reject)
epochs.plot_drop_log() 

Is this correct? I would use it, as alternative to the autoreject method, to reject epochs based on peak-to-peak signal amplitude (PTP).

In my case, it produced the 68.2% of epochs rejected. Do you have some tips in order to try to reduce such huge percentage of epoch rejected?

In general my aim, from the beginning, was to clean up the EEG signal from artefacts, in order to get clean and usable epochs. In order to do that I performed the following operations (now improved thanks to your help):

  • filtering between 1Hz and 30Hz
  • ICA
  • Epochs rejected according to the PTP

Do we have some other methods in MNE I could integrate in order to further clean the EEG signal from artefacts? (I’m already aware about the method related to Heartbeat artifacts (ECG) even if it should not make huge change on EEG data )

thank you

Your rejection threshold is way too low. The idea of the reject parameter is to exclude only the most extreme, unusual artifacts. For EEG, I would typically set it to 100e-6, 150e-6, or sometimes even 250e-6, depending on the data.

It’s very difficult or even impossible to give general advice on processing a dataset one doesn’t have available, and also the (pre)processing steps to take typically depend on the planned analysis one wishes to conduct. So I cannot provide more advice at this time. I suggest you discuss the analysis plan with your PI.

Great!

  • 100e-6 led to 15.6 % of rejected epochs
  • 150e-6 led to 4.6 % of rejected epochs
    Thank you!

Just be sure not to lose the epochs that have high amplitudes due to blinks; after all, that’s why you’re doing ICA in the first place: so you don’t have to remove the epochs with blinks.

Following your tips, first I epoched data, then I run the ICA on epochs (removing the ocular component and reconstructing the signal) and only afterwards I apply the rejection dictionary to epochs as follow:

# Reject epochs (MNE method)
reject = dict(eeg=100e-6)  # unit: V (EEG channels)
epochs.drop_bad(reject=reject)
epochs.plot_drop_log()

Doing that, the rejection dictionary is applied on epochs that are already free from blinks, so should be safe… correct?

Perfect, this is a good choice! :slight_smile: