
Signal processing and filtering are tasks when analyzing and cleaning data from sensors, audio signals, and other noisy sources. SciPy, the popular Python library for scientific computing, provides handy tools for efficiently filtering and transforming signal data. Here’s how.
(Note: the article assumes the reader’s familiarity with the fundamentals of signal data and their processing)
Key SciPy Methods for Signal Processing and Filtering
The following are some useful methods that SciPy’s signal package provides to apply different processing and filtering techniques on signal data.
- scipy.signal.butter: it applies a Butterworth filter for smoothing a signal based on frequencies, concretely by removing unwanted frequencies (noise) while keeping desired frequency components
- scipy.signal.filtfilt: this method applies a filter to a signal twice: forward and backward. This way, any phase distortions caused by filtering are removed, so it constitutes another approach to removing unwanted frequencies.
- scipy.signal.welch: it uses Welch’s frequency analysis approach to estimate the Power Spectral Density (PSD) of a signal. The PSD is useful for analyzing how the power of a signal is distributed across frequency components, helping determine dominant frequencies and patterns.
- scipy.signal.find_peaks: a method to identify signal peaks, useful to locate local maxima or other salient patterns and events.
- scipy.signal.spectrogram: computes a spectrogram for visualizing the frequency content of a signal over time.
Application Example
Let’s see now how to use some of these SciPy functions in practice.
First, we will import the necessary library packages and methods.
import numpy as np import matplotlib.pyplot as plt from scipy.signal import butter, filtfilt
Next, the below code generates a sample of signal data, namely a 1-second sine wave with some random noise added.
fs = 500 # Sampling frequency (Hz) t = np.linspace(0, 1, fs, endpoint=False) freq_signal = 5 # Signal frequency (Hz) noise = np.random.normal(0, 0.5, fs) signal = np.sin(2 * np.pi * freq_signal * t) + noise
Using SciPy, we now define and apply a low-pass 4th-order Butterworth filter. The method takes four arguments: the filter order N (with higher N leading to steeper filters), a cutoff frequency, the filter type, and the sampling frequency of the signal. It returns two outputs: the numerator and denominator coefficients of the transfer function, which state how the input signal is transformed.
cutoff = 10 b, a = butter(N=4, Wn=cutoff, btype='low', fs=fs)
The outputs are then used to apply a forward and backward filter to the original signal.
filtered_signal = filtfilt(b, a, signal)
Finally, we use matplotlib to jointly display both the original signal and the filtered signal.
plt.figure(figsize=(10, 5))
plt.plot(t, signal, label='Noisy Signal', alpha=0.7)
plt.plot(t, filtered_signal, label='Filtered Signal', linewidth=2)
plt.title("Low-Pass Filtering with SciPy")
plt.xlabel("Time (s)")
plt.ylabel("Amplitude")
plt.legend()
plt.grid()
plt.show()
The resulting plot:

To wrap up, we’ll compute and show the PSD of the initial noisy signal and the filtered signal.
from scipy.signal import welch
f_original, Pxx_original = welch(signal, fs=fs, nperseg=256)
f_filtered, Pxx_filtered = welch(filtered_signal, fs=fs, nperseg=256)
plt.figure(figsize=(10, 5))
plt.semilogy(f_original, Pxx_original, label='Original Signal')
plt.semilogy(f_filtered, Pxx_filtered, label='Filtered Signal')
plt.title("Power Spectral Density Before and After Filtering")
plt.xlabel("Frequency (Hz)")
plt.ylabel("Power Spectral Density")
plt.legend()
plt.grid()
plt.show()

The PSD plot shows how the low-pass filter removed the high-frequency noise from the signal while preserving the low-frequency components.
