-1
$\begingroup$

In the plot below the blue wave is the original signal. The orange wave is after the low pass filter. The green wave is after the high pass filter. The blue wave is the original signal. The orange wave is after the low pass filter. The green wave is after the high pass filter

I have a noisy ECG signal stored in the ecg_val column of a pandas dataframe. I'm doing the following:

# implement the 30 Hz low-pass forward-backward filter
__nyq = 0.5 * self.resample_frequency
__normal_lowpass_cutoff = self.lowpass_cutoff / __nyq
__order = 5
b, a = signal.butter(__order, __normal_lowpass_cutoff, btype='low')
self.ecg['ecg_val'] = signal.filtfilt(b, a, self.ecg['ecg_val'])

plt.plot(self.ecg['hexoskin_timestamps'], self.ecg['ecg_val'])

# implement the 1.4 Hz high-pass forward-backward filter
__nyq = 0.5 * self.resample_frequency
__normal_highpass_cutoff = self.highpass_cutoff / __nyq
__order = 2
b, a = signal.butter(__order, __normal_highpass_cutoff, btype='high', analog=False)
self.ecg['ecg_val'] = signal.filtfilt(b, a, self.ecg['ecg_val'])

plt.plot(self.ecg['hexoskin_timestamps'], self.ecg['ecg_val'])

plt.show()

As you can see, the application of the low-pass forward-backward filter doesn't change the amplitude of the original signal. But the high-pass forward-backward filter begins at 0 - which I wouldn't want happening.

How do I change this?

The Scipy implementation of filtfilt from here is as follows

def filtfilt(b, a, x, axis=-1, padtype='odd', padlen=None, method='pad'):
    b = np.atleast_1d(b)
    a = np.atleast_1d(a)
    x = np.asarray(x)

    # method == "pad"
    edge, ext = _validate_pad(padtype, padlen, x, axis,
                              ntaps=max(len(a), len(b)))

    # Get the steady state of the filter's step response.
    zi = lfilter_zi(b, a)

    # Reshape zi and create x0 so that zi*x0 broadcasts
    # to the correct value for the 'zi' keyword argument
    # to lfilter.
    zi_shape = [1] * x.ndim
    zi_shape[axis] = zi.size
    zi = np.reshape(zi, zi_shape)
    x0 = axis_slice(ext, stop=1, axis=axis)

    # Forward filter.
    (y, zf) = lfilter(b, a, ext, axis=axis, zi=zi * x0)

    # Backward filter.
    # Create y0 so zi*y0 broadcasts appropriately.
    y0 = axis_slice(y, start=-1, axis=axis)
    (y, zf) = lfilter(b, a, axis_reverse(y, axis=axis), axis=axis, zi=zi * y0)

    # Reverse y.
    y = axis_reverse(y, axis=axis)

    if edge > 0:
        # Slice the actual signal from the extended signal.
        y = axis_slice(y, start=edge, stop=-edge, axis=axis)

    return y
$\endgroup$
1
$\begingroup$

It looks like your signal has a DC offset, i.e. its average is non-zero (approximately $1$ in this case). This component is captured by the 0'th frequency bin. So if a high-pass filter is applied to the signal, all components below the cut-off frequency (1.4 Hz) will be removed, including the DC one (0 Hz).

In general, you should always remove the mean of your signal before performing any sort of analysis. I am not sure about ECG applications, but it should be no different. Please try to remove it from both blue and orange signal.

A slight off-topic, but there are very narrow filters which are designed to specifically remove the 0 Hz component. For example you can read more about them here and here.

$\endgroup$
  • $\begingroup$ Comments are not for extended discussion; this conversation has been moved to chat. $\endgroup$ – jojek Jun 11 '17 at 11:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.