NeuroKit
NeuroKit copied to clipboard
Problem with EDA analysis because of low sampling rate (Empatica E4)
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!
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 theeda_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 useeda_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 onemethod
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 likemethod_clean
;method_peaks
etc.? And for instance, ifmethod_clean
isNone
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
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.