Repository: mne-tools/mne-python

Issue:
mne.preprocessing.find_ecg_events has a non-descript error when it can't detect ecg events
#### Describe the bug
mne.preprocessing.find_ecg_events has a non-descript error when it can't detect ecg events


#### Steps to reproduce
Should work with just a straight line or zeros in an ECG channel. 


#### Expected results
Either return an empty array or generate a more descriptive error message


#### Actual results
line 230, in find_ecg_events
    ecg_events = remap[ecg_events]
IndexError: arrays used as indices must be of integer (or boolean) type

(This is because ecg_events is None)
#### Additional information
Platform:      Windows-10-10.0.19041-SP0
Python:        3.7.9 (default, Aug 31 2020, 17:10:11) [MSC v.1916 64 bit (AMD64)]
Executable:   ...python.exe
CPU:           Intel64 Family 6 Model 158 Stepping 13, GenuineIntel: 8 cores
Memory:        Unavailable (requires "psutil" package)
mne:           0.22.0
numpy:         1.19.2 {blas=mkl_rt, lapack=mkl_rt}
scipy:         1.5.2
matplotlib:    3.3.2 {backend=Qt5Agg}
sklearn:       0.24.1
numba:         0.52.0
nibabel:       Not found
nilearn:       Not found
dipy:          Not found
cupy:          Not found
pandas:        1.1.5
mayavi:        Not found
pyvista:       Not found
vtk:           Not found




Discussion:
can you send a PR?

thx

>


Resolving patch:
diff --git a/mne/preprocessing/ecg.py b/mne/preprocessing/ecg.py
--- a/mne/preprocessing/ecg.py
+++ b/mne/preprocessing/ecg.py
@@ -49,7 +49,7 @@ def qrs_detector(sfreq, ecg, thresh_value=0.6, levels=2.5, n_thresh=3,
     Returns
     -------
     events : array
-        Indices of ECG peaks
+        Indices of ECG peaks.
     """
     win_size = int(round((60.0 * sfreq) / 120.0))
 
@@ -112,20 +112,27 @@ def qrs_detector(sfreq, ecg, thresh_value=0.6, levels=2.5, n_thresh=3,
         ce = time[b[a < n_thresh]]
 
         ce += n_samples_start
-        clean_events.append(ce)
-
-    # pick the best threshold; first get effective heart rates
-    rates = np.array([60. * len(cev) / (len(ecg) / float(sfreq))
-                      for cev in clean_events])
-
-    # now find heart rates that seem reasonable (infant through adult athlete)
-    idx = np.where(np.logical_and(rates <= 160., rates >= 40.))[0]
-    if len(idx) > 0:
-        ideal_rate = np.median(rates[idx])  # get close to the median
+        if ce.size > 0:  # We actually found an event
+            clean_events.append(ce)
+
+    if clean_events:
+        # pick the best threshold; first get effective heart rates
+        rates = np.array([60. * len(cev) / (len(ecg) / float(sfreq))
+                          for cev in clean_events])
+
+        # now find heart rates that seem reasonable (infant through adult
+        # athlete)
+        idx = np.where(np.logical_and(rates <= 160., rates >= 40.))[0]
+        if idx.size > 0:
+            ideal_rate = np.median(rates[idx])  # get close to the median
+        else:
+            ideal_rate = 80.  # get close to a reasonable default
+
+        idx = np.argmin(np.abs(rates - ideal_rate))
+        clean_events = clean_events[idx]
     else:
-        ideal_rate = 80.  # get close to a reasonable default
-    idx = np.argmin(np.abs(rates - ideal_rate))
-    clean_events = clean_events[idx]
+        clean_events = np.array([])
+
     return clean_events
 
 
@@ -219,7 +226,11 @@ def find_ecg_events(raw, event_id=999, ch_name=None, tstart=0.0,
         remap[offset:offset + this_len] = np.arange(start, stop)
         offset += this_len
     assert offset == len(ecg)
-    ecg_events = remap[ecg_events]
+
+    if ecg_events.size > 0:
+        ecg_events = remap[ecg_events]
+    else:
+        ecg_events = np.array([])
 
     n_events = len(ecg_events)
     duration_sec = len(ecg) / raw.info['sfreq'] - tstart


Test patch:
diff --git a/mne/preprocessing/tests/test_ecg.py b/mne/preprocessing/tests/test_ecg.py
--- a/mne/preprocessing/tests/test_ecg.py
+++ b/mne/preprocessing/tests/test_ecg.py
@@ -1,5 +1,6 @@
 import os.path as op
 import pytest
+import numpy as np
 
 from mne.io import read_raw_fif
 from mne import pick_types
@@ -84,3 +85,13 @@ def test_find_ecg():
     assert len(ecg_epochs.events) == n_events
     assert 'ECG-SYN' not in raw.ch_names
     assert 'ECG-SYN' not in ecg_epochs.ch_names
+
+    # Test behavior if no peaks can be found -> achieve this by providing
+    # all-zero'd data
+    raw._data[ecg_idx] = 0.
+    ecg_events, _, average_pulse, ecg = find_ecg_events(
+        raw, ch_name=raw.ch_names[ecg_idx], return_ecg=True
+    )
+    assert ecg_events.size == 0
+    assert average_pulse == 0
+    assert np.allclose(ecg, np.zeros_like(ecg))