Problem finding and removing EOG artifacts

Hi all!
I have some trouble with removing EOG blinks. The code I am using to select EOG events:

#%% ICA; removal EOG artefacts

#visualize EOG artifacts 
eog_evoked = mne.preprocessing.create_eog_epochs(raw_filtered_notch_two, thresh = 1e-15).average()
eog_evoked.apply_baseline((None, None))

#set up ICA
ica = mne.preprocessing.ICA(n_components=64, max_iter = 'auto', method = 'infomax', random_state=97).fit(epochs)

#find which ICs match the EOG pattern
ica.exclude = []
eog_indices, eog_scores = ica.find_bads_eog(epochs, threshold = 2)
ica.exclude = eog_indices

# Look at IC components 
ica.plot_scores(eog_scores) #ICA component cosen
ica.plot_properties(epochs, picks=eog_indices)

#apply ica to data
ica.apply(epochs, exclude = eog_indices)

I have several problems:

  1. In some cases, the EOG artefacts look really odd and most of the time only a few (around 20 instead of around 700, which is more common) blinks are detected. I already tried to play around with the low & high pass frequency and threshold, but with no succes. When I set the threshold on 1e-15 instead of auto, more eog (a lot more, around 9000, which seems a lot?) are found, but the deviant shape stays present.

  1. Sometimes the ICA component selected is not representative for the EOG activity.

Any advice?
Thanks in advance

Hello @Evelyne_Fraats,

the EOG evoked plots you posted all show that the EOG peak detection algorithm didn’t work as intended; hence for now you can ignore all things ICA, as you first need to focus on finding the ocular activity.

create_eog_epochs() by default applies reasonable frequency filters, so you shouldn’t have to precondition the data with frequency or notch filtering.

Does your data consist of a long recording that spans several experimental blocks, with breaks in-between blocks? If yes, then the movement artifacts during breaks could throw off the peak detector (there are ways to avoid this issue, though).

Best wishes,

Dear @richard,

The data variable I put in create_eog_epochs() is indeed filtered. However, when I run the function on my raw_ref data variable (only referenced) I get the same output. I do not have this EOG issue with all my recording, only for 10 out of 75 recordings, therefore I do not think the problem is in the way I perform the EOG correction. I rather think it has to do something with poor contact of EOG electrodes or a very high noise on EOG electrodes or lots of big artifacts.

My data does not cosist of long recording with several blocks, it consist of a 30min recording, with 540 images present in sequences. In between these sequences, participants could have a break. So possibly, these artifacts throw off my data. Is it possible to only look at EOG events in my epoch time window?

The first thing to do is check which bits of the signal were actually classified as EOG peaks.

To do this, you can use mne.preprocessing.find_eog_events(), which is also called internally by create_eog_epochs(). This function returns an events array.

You can then use this information to plot the EOG events on top of your raw data, by doing something like:

eog_events = mne.preprocessing.find_eog_events(raw, event_id=998)
raw.plot(events=eog_events, event_id={'EOG': 998})

and then check carefully where the events were placed.

Dear @richard ,

Whenever the EOG shows a odd pattern, there are always very few eye blinks found (around 5 to 20 for a recording of 30min). Therefore there are not many events plotted on the raw data and do not capture all eye blinks (obviously). When an EOG event is marked, this is for random activity (see images)

Random EOG detection:
eog bad random

Lacking EOG detection:
epoch bad

Correct EOG detection:
epch save

Hello @Evelyne_Fraats, I think at this stage it’s more helpful to focus on the EOG channels only, because these are going to be used to run the peak detection. So you’ll want to do something like,

eog_events = mne.preprocessing.find_eog_events(raw, event_id=998)

    .plot(events=eog_events, event_id={'EOG': 998})

and carefully check the EOG data, while ignoring all other channels for now.

After changing the threshold of mne.preprocessing.create_eog_epochs to 90e-6 and the rejection rate to a value between 250e-6 and 500e-6 (to what best fit the data), it is solved.