How To apply a filter to a signal in python
Asked Answered
R

2

26

is there any prepared function in python to apply a filter (for example Butterworth filter) to a given signal? I looking for such a function in 'scipy.signal' but I haven't find any useful functions more than filter design ones. actually I want this function to convolve a filter with the signal.

Rimini answered 6/12, 2012 at 9:16 Comment(2)
What have you tried? lfilter can implement any LTI filter you might dream up.Glogau
See also: scipy.org/Cookbook/ButterworthBandpass, scipy.org/Cookbook/FiltFilt, scipy.org/Cookbook/FIRFilter, scipy.org/Cookbook/ApplyFIRFilterChappelka
S
45

Yes! There are two:

scipy.signal.filtfilt
scipy.signal.lfilter

There are also methods for convolution (convolve and fftconvolve), but these are probably not appropriate for your application because it involves IIR filters.

Full code sample:

b, a = scipy.signal.butter(N, Wn, 'low')
output_signal = scipy.signal.filtfilt(b, a, input_signal)

You can read more about the arguments and usage in the documentation. One gotcha is that Wn is a fraction of the Nyquist frequency (half the sampling frequency). So if the sampling rate is 1000Hz and you want a cutoff of 250Hz, you should use Wn=0.5.

By the way, I highly recommend the use of filtfilt over lfilter (which is called just filter in Matlab) for most applications. As the documentation states:

This function applies a linear filter twice, once forward and once backwards. The combined filter has linear phase.

What this means is that each value of the output is a function of both "past" and "future" points in the input equally. Therefore it will not lag the input.

In contrast, lfilter uses only "past" values of the input. This inevitably introduces a time lag, which will be frequency-dependent. There are of course a few applications for which this is desirable (notably real-time filtering), but most users are far better off with filtfilt.

Synodic answered 6/12, 2012 at 9:27 Comment(0)
B
0

While the explanation in @cxrodgers' answer is correct for Infinite Impulse Response (IIR) filters (and Butterworth is an example of such filter type). There is more in "How to apply a filter to a signal in python" that what has been answered so far. I believe that a bit of theoretical explanation is required so that the inexperienced person reading the accepted answer doesn't think that it is always the correct solution to always apply scipy.signal.filtfilt when filtering in Python which for a Finite Impulse Response (FIR) filter would be unnecessary.

Realizable (that can be implemented in practice) IIR filters always have non-linear phase. This means that the entry order of different frequency components at its input would be shifted at its output (I think @cxrodgers is referring to this as "lag the input"). It has been proven that IIR filters can theoretically have linear phase but the coefficients would need to be irrational so this is not realizable. More info here

As a way to compensate this non-linear phase effect scipy.signal.filtfilt applies a method called zero-phase filtering which applies the IIR filter in one direction, then reverses it and applies it again, compensating in the second pass the non-linearity introduced in the first pass. This process takes additional time so it may not be feasible when processing real time applications (depending on how fast you can perform both filtering operations compared to how fast new data comes)

However FIR filters are typically designed to have linear phases (this means its coefficients in time are symmetrical around the center coefficient, that is, the first coefficient is the same as the last; the second is the same as the next-to-last, etc) More info here

So when applying an FIR filter in Python, the only right answer is to use scipy.signal.lfilter. There is no need to compensate again an already linear phase. See an example below applying a band pass Kaiser FIR filter. More examples here

import numpy as np
from scipy import signal as sp_signal

#defining parameters
Fs = 48000
duration = 1 #seconds
time = np.arange(0,duration,1/Fs)
cols = time.size
np.random.seed(0)

# defining signals
sig_mean = 0
sig_std = 1 

sig1 = np.random.normal(sig_mean, sig_std, cols)

# band pass Kaiser FIR filtering
ntaps = 128
width = 1.0 
nyq = 0.5*Fs
atten = sp_signal.kaiser_atten(numtaps=ntaps,width=width/nyq) #desired width of the transition region between passband and stopband expressed as a fraction of the Nyquist frequency
beta = sp_signal.kaiser_beta(atten)
b = sp_signal.firwin(numtaps=ntaps,cutoff=[1000, 2000],nyq=nyq,pass_zero=False,window=('kaiser', beta), scale=False)
a = 1

# filtered signal
sig2 = sp_signal.lfilter(b, a, sig1) 
Bryon answered 25/7, 2023 at 17:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.