NeuroKit icon indicating copy to clipboard operation
NeuroKit copied to clipboard

Problem with EDA analysis because of low sampling rate (Empatica E4)

Open FMJanot opened this issue 2 years ago • 3 comments

Hi everyone, I am trying to use NeuroKit2 for EDA analysis. However, there is one problem I have encountered: When trying to process the data, I get an error message that says:

processedData, info = nk.eda_process(datenEDAAnalyse, sampling_rate=4, method='neurokit')

…\anaconda3\lib\site-packages\neurokit2\signal\signal_filter.py:269: NeuroKitWarning: The sampling rate is too low. Sampling rate must exceed the Nyquist rate to avoid aliasing problem. In this analysis, the sampling rate has to be higher than 6 Hz
  warn(

Traceback (most recent call last):

  File "<ipython-input-9-8dd853df2e84>", line 1, in <module>
    processedData, info = nk.eda_process(datenEDAAnalyse, sampling_rate=4, method='neurokit')

  File "…\anaconda3\lib\site-packages\neurokit2\eda\eda_process.py", line 77, in eda_process
    eda_cleaned = eda_clean(eda_signal, sampling_rate=sampling_rate, method=method)
  
File "…\anaconda3\lib\site-packages\neurokit2\eda\eda_clean.py", line 62, in eda_clean
    clean = _eda_clean_neurokit(eda_signal, sampling_rate)
 
 File "…\anaconda3\lib\site-packages\neurokit2\eda\eda_clean.py", line 84, in _eda_clean_neurokit
    filtered = signal_filter(eda_signal, sampling_rate=sampling_rate, highcut=3, method="butterworth", order=4)
  
File "…\anaconda3\lib\site-packages\neurokit2\signal\signal_filter.py", line 131, in signal_filter
    filtered = _signal_filter_butterworth(signal, sampling_rate, lowcut, highcut, order)
 
 File "…\anaconda3\lib\site-packages\neurokit2\signal\signal_filter.py", line 211, in _signal_filter_butterworth
    sos = scipy.signal.butter(order, freqs, btype=filter_type, output="sos", fs=sampling_rate)
 
 File "…\anaconda3\lib\site-packages\scipy\signal\filter_design.py", line 2955, in butter
    return iirfilter(N, Wn, btype=btype, analog=analog,

  File "…\anaconda3\lib\site-packages\scipy\signal\filter_design.py", line 2421, in iirfilter
    raise ValueError("Digital filter critical frequencies "

ValueError: Digital filter critical frequencies must be 0 < Wn < fs/2 (fs=4 -> fs/2=2.0)

Apparently the sampling rate is too low. I am working with the Empatica E4 wristband. The E4 has a sampling rate of 4 Hz, which cannot be customized. Do you happen to know any way how to solve this problem so that we can continue to work with the NeuroKit2 package? Is it possible to adjust the code in such a way that it will work with 4 Hz?

I'd be happy about any hints or recommendations! Thanks!

FMJanot avatar Sep 22 '21 17:09 FMJanot

Hi @FMJanot, welcome to github

As you can see; the SR of your data is below NK's default cleaning method filtering range:

https://github.com/neuropsychology/NeuroKit/blob/c1104386655724a5c624e740abf651c939fc5e48/neurokit2/eda/eda_clean.py#L81-L84

You could try using the other method, biosppy. The two other possible workarounds that I see currently are:

  • Create your own custom processing pipeline: instead of using eda_process, use directly the subfunctions (and skip the cleaning step). You can see what the eda_process function does here: https://github.com/neuropsychology/NeuroKit/blob/c1104386655724a5c624e740abf651c939fc5e48/neurokit2/eda/eda_process.py#L76-L78
  • Upsample the signal using signal_resample. This will essentially interpolate the points to give you an artificially larger sampling rate (note that the amount of information is the same). You should be then able to use eda_process

Few things for us (@zen-juen @Tam-Pham):

  • [x] We might want to throw more informative warnings in signal_filter; like if the SR is below the range we could 1) throw a warning (about the nyquist thing, print what the current SR is and what the minimum should be) and 2) return the unfiltered signal. This way it would not error but users would be notified that the signal has not been filtered (and why)
  • [x] Add more details in eda_clean docstrings about the two currently available methods
  • [ ] I'm wondering if we should make the method dispatch more flexible in *_process: for now we usually only have one method argument that goes on to the different steps, but usually different methods are available and might be tweaked separately. Maybe we should add some consistent API like method_clean; method_peaks etc.? And for instance, if method_clean is None then skip the cleaning?
  • [ ] Why do we have this frequency variable initiated to 5 like so instead of directly within the next line 🤔

https://github.com/neuropsychology/NeuroKit/blob/c1104386655724a5c624e740abf651c939fc5e48/neurokit2/eda/eda_clean.py#L100-L101

DominiqueMakowski avatar Sep 22 '21 23:09 DominiqueMakowski

Like @DominiqueMakowski suggested you can use your own custom processing pipeline. Here is what I used instead of the 'eda_process' function. Every other EDA function can work as expected if you use this custom processing pipeline for data from Empatica.

def eda_custom_process(eda_signal, sampling_rate=4, method="neurokit"):
    

    eda_signal = nk.signal_sanitize(eda_signal)
    
    # Series check for non-default index
    if type(eda_signal) is pd.Series and type(eda_signal.index) != pd.RangeIndex:
        eda_signal = eda_signal.reset_index(drop=True)
    
    # Preprocess
    eda_cleaned = eda_signal  #Add your custom cleaning module here or skip cleaning
    eda_decomposed = nk.eda_phasic(eda_cleaned, sampling_rate=sampling_rate)

    # Find peaks
    peak_signal, info = nk.eda_peaks(
        eda_decomposed["EDA_Phasic"].values,
        sampling_rate=sampling_rate,
        method=method,
        amplitude_min=0.1,
    )
    info['sampling_rate'] = sampling_rate  # Add sampling rate in dict info

    # Store
    signals = pd.DataFrame({"EDA_Raw": eda_signal, "EDA_Clean": eda_cleaned})

    signals = pd.concat([signals, eda_decomposed, peak_signal], axis=1)

    return signals, info

I basically skip the eda_clean step, and use my own cleaning modules. You can also skip cleaning the signal overall.

isayasMatter avatar Nov 02 '21 18:11 isayasMatter