mne-tools / mne-python

MNE: Magnetoencephalography (MEG) and Electroencephalography (EEG) in Python
https://mne.tools
BSD 3-Clause "New" or "Revised" License
2.68k stars 1.31k forks source link

find_eog_events() & find_ecg_events() find events when there are literally none #9273

Open hoechenberger opened 3 years ago

hoechenberger commented 3 years ago

Problem description & MWE

import os
import mne

sample_data_folder = mne.datasets.sample.data_path()
sample_data_raw_file = os.path.join(sample_data_folder, 'MEG', 'sample',
                                    'sample_audvis_raw.fif')
raw = mne.io.read_raw_fif(sample_data_raw_file, preload=True)

# Replace all EOG data with 1's, and drop all channels but EOG
raw._data[raw.ch_names.index('EOG 061'), :] = 1.
raw.pick_channels(['EOG 061'])

# EOG detection finds ocular activity
eog_events = mne.preprocessing.find_eog_events(raw, ch_name='EOG 061')
print(eog_events)

Output:

Found 560 significant peaks
Number of EOG events detected : 560
[[ 25847      0    998]
 [ 26542      0    998]
 [ 27055      0    998]
 ...
 [191898      0    998]
 [192414      0    998]
 [192599      0    998]]

We can even create Epochs …

eog_epochs = mne.Epochs(raw=raw, events=eog_events)
eog_epochs.plot(picks='eog')
Screen Shot 2021-04-09 at 10 54 00

I assumed this has something to do with the filtering that happens in find_eog_events(), so I changed the filter length:

eog_events = mne.preprocessing.find_eog_events(raw, ch_name='EOG 061',
                                               filter_length='30s')

Output:

Found 140 significant peaks
Number of EOG events detected : 140

Ok so I thought the problem could be related to small numerical fluctuations during filtering. So I looked at the thresholding we use for peak detection:

    thresh : float | None
        Threshold to trigger the detection of an EOG event. This controls the
        thresholding of the underlying peak-finding algorithm. Larger values
        mean that fewer peaks (i.e., fewer EOG events) will be detected.
        If ``None``, use the default of ``(max(eog) - min(eog)) / 4``,
        with ``eog`` being the filtered EOG signal.

And with the following, all went as expected:

eog_events = mne.preprocessing.find_eog_events(raw, ch_name='EOG 061',
                                               thresh=1e-15)

Output:

No significant peaks found
Number of EOG events detected : 0

Bingo.

Proposal

thresh should get a lower boundary, so it cannot become "0." and floating point imprecisions won't trigger peak detection.

hoechenberger commented 3 years ago

Friendly ping @agramfort @larsoner, I need advice on how to best approach this :)

hoechenberger commented 3 years ago

I just ran into the same problem with find_ecg_events with qrs_thresh set to 'auto' (default); setting it to 1e-15 fixed it.

hoechenberger commented 3 years ago

Discussion with @agramfort: I'll implement a thresholding at 100 * eps of the respective dtype.

AaronAngJW commented 3 years ago

Hi Richard, can I just check if this only affects data that have non-existent ECG and EOG data? (I'm assuming that is the scenario based on the empty epoch plot above); or spurious ECG/EOG events can also arise at a non-negligible level in a data with present ECG and EOG activity? I am asking because these events are primary to my current work.