NeuroKit icon indicating copy to clipboard operation
NeuroKit copied to clipboard

engzeemod2012 method of ecg_clean() fails with low sampling rate

Open danibene opened this issue 1 year ago • 5 comments

Describe the bug When the input ECG signal has a low sampling rate, the "engzeemod2012" method of the ecg_clean() function does not work:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
[<ipython-input-10-7058798300e2>](https://localhost:8080/#) in <module>()
      1 sampling_rate = 100
      2 ecg_signal = nk.ecg.ecg_simulate(sampling_rate=sampling_rate)
----> 3 clean_ecg_signal = nk.ecg_clean(ecg_signal, sampling_rate=sampling_rate, method="engzeemod2012")

3 frames
[/usr/local/lib/python3.7/dist-packages/neurokit2/ecg/ecg_clean.py](https://localhost:8080/#) in ecg_clean(ecg_signal, sampling_rate, method, **kwargs)
    108         clean = _ecg_clean_elgendi(ecg_signal, sampling_rate)
    109     elif method in ["engzee", "engzee2012", "engzeemod", "engzeemod2012"]:
--> 110         clean = _ecg_clean_engzee(ecg_signal, sampling_rate)
    111     elif method in [
    112         "christov",

[/usr/local/lib/python3.7/dist-packages/neurokit2/ecg/ecg_clean.py](https://localhost:8080/#) in _ecg_clean_engzee(ecg_signal, sampling_rate)
    260     f2 = 52 / (0.5 * sampling_rate)
    261 
--> 262     sos = scipy.signal.butter(4, [f1, f2], btype="bandstop", output="sos")
    263     zi_coeff = scipy.signal.sosfilt_zi(sos)
    264     zi = zi_coeff * np.mean(ecg_signal)

[/usr/local/lib/python3.7/dist-packages/scipy/signal/filter_design.py](https://localhost:8080/#) in butter(N, Wn, btype, analog, output, fs)
   2955     """
   2956     return iirfilter(N, Wn, btype=btype, analog=analog,
-> 2957                      output=output, ftype='butter', fs=fs)
   2958 
   2959 

[/usr/local/lib/python3.7/dist-packages/scipy/signal/filter_design.py](https://localhost:8080/#) in iirfilter(N, Wn, rp, rs, btype, analog, ftype, output, fs)
   2422                 raise ValueError("Digital filter critical frequencies "
   2423                                  "must be 0 < Wn < fs/2 (fs={} -> fs/2={})".format(fs, fs/2))
-> 2424             raise ValueError("Digital filter critical frequencies "
   2425                              "must be 0 < Wn < 1")
   2426         fs = 2.0

ValueError: Digital filter critical frequencies must be 0 < Wn < 1

To Reproduce To get the error above, I ran the following code:

import neurokit2 as nk
sampling_rate = 100
ecg_signal = nk.ecg.ecg_simulate(sampling_rate=sampling_rate)
clean_ecg_signal = nk.ecg_clean(ecg_signal, sampling_rate=sampling_rate, method="engzeemod2012")

Expected behaviour Maybe a warning telling me to resample?

System Specifications

- OS: Linux ( 64bit) 
- Python: 3.7.13 
- NeuroKit2: 0.2.0 

- NumPy: 1.21.6 
- Pandas: 1.3.5 
- SciPy: 1.7.3 
- sklearn: 1.0.2 
- matplotlib: 3.2.2

danibene avatar Jul 22 '22 03:07 danibene

If the sampling rate is too low / high to achieve a given bandpass filtering we should just apply a highcut / lowcut (depending on what's possible) no? also perhaps we should use signal_filter here? Initially I wanted to use the exact same functions as used in other implementations (e.g., biosppy), but now I am thinking that if an implementation is not optimal we might as well update it

DominiqueMakowski avatar Jul 22 '22 04:07 DominiqueMakowski

If the sampling rate is too low / high to achieve a given bandpass filtering we should just apply a highcut / lowcut (depending on what's possible) no? also perhaps we should use signal_filter here?

@DominiqueMakowski Yeah that works! Should that be a general change to signal_filter() then?

There's already a warning but it doesn't change the critical frequencies: https://github.com/neuropsychology/NeuroKit/blob/277f614ee3984647d71ba31aadffea97766aeb0f/neurokit2/signal/signal_filter.py#L305-L315

Also it only checks highcut, but in the case of filter_type = "bandstop", lowcut should also be checked:

https://github.com/neuropsychology/NeuroKit/blob/277f614ee3984647d71ba31aadffea97766aeb0f/neurokit2/signal/signal_filter.py#L325-L326

danibene avatar Jul 22 '22 05:07 danibene

Actually, I don't understand how a bandstop filter is supposed to be indicated with the current implementation of signal_filter, since lowcut shouldn't be higher than highcut for the underlying default scipy function? See https://gist.github.com/danibene/12e95a7cdf881d5fc1a8eab742ffa0f2

danibene avatar Jul 22 '22 05:07 danibene

Is this still relevant? If so, what is blocking it? Is there anything you can do to help move it forward?

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.

stale[bot] avatar Sep 20 '22 19:09 stale[bot]

i'm gonna modify stale bot's setting so that it doesn't close the issues...

but this issue has been addressed right?

DominiqueMakowski avatar Sep 20 '22 23:09 DominiqueMakowski