# Forward Backward filter Scipy - $\tt signal.filtfilt$ changes the amplitude when $\tt signal.butter$ is used with $\tt btype='high'$

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.

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)

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


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).