Recently, I was working on a signal processing project where I needed to remove noise from sensor data collected from a traffic monitoring system in downtown Chicago. The challenge was filtering out random fluctuations while preserving the important traffic pattern information. This is where the Butterworth filter in SciPy came to my rescue.
In this article, I’ll walk you through everything you need to know about implementing Butterworth filters using Python’s SciPy library. Whether you’re cleaning noisy signals, processing images, or analyzing time-series data, this powerful filter can be a game-changer.
So let’s start..!
What is a Butterworth Filter?
The Butterworth filter is a type of signal processing filter designed to have a frequency response as flat as possible in the passband. It’s often called a maximally flat magnitude filter.
Unlike other filters that might create ripples in the passband, the Butterworth filter maintains a smooth response, making it ideal for many real-world applications.
When I first started using it for cleaning economic time series data, I was impressed by how well it preserved the underlying trends while removing high-frequency noise.
Method 1: Basic Butterworth Filter Implementation
Let’s start with a simple example of applying a low-pass Butterworth filter to a noisy signal:
import numpy as np
from scipy import signal
import matplotlib.pyplot as plt
# Create a noisy signal
t = np.linspace(0, 1, 1000, False) # 1 second
sig = np.sin(2*np.pi*10*t) + np.sin(2*np.pi*20*t) + 0.5*np.random.randn(len(t))
# Design the Butterworth filter
order = 4 # Filter order
cutoff = 15 # Cutoff frequency in Hz
b, a = signal.butter(order, cutoff, 'low', fs=1000)
# Apply the filter
filtered_sig = signal.filtfilt(b, a, sig)
# Plot the results
plt.figure(figsize=(10, 6))
plt.plot(t, sig, 'b-', label='Noisy signal')
plt.plot(t, filtered_sig, 'r-', label='Filtered signal')
plt.legend()
plt.xlabel('Time [s]')
plt.ylabel('Amplitude')
plt.title('Butterworth Low-Pass Filter')
plt.grid()
plt.show()You can see the output in the screenshot below.

In this example, I’ve created a signal with two sine waves (10 Hz and 20 Hz) and added random noise. The Butterworth filter effectively removes the 20 Hz component and the noise, leaving a clean 10 Hz sine wave.
The signal.butter() function creates the filter coefficients, and signal.filtfilt() applies the filter in forward and backward directions, resulting in zero-phase filtering.
Method 2: High-Pass Butterworth Filter
Sometimes you need to remove low-frequency components while keeping high-frequency ones. Here’s how to implement a high-pass filter:
# Design a high-pass Butterworth filter
order = 4
cutoff = 15 # Cutoff frequency in Hz
b, a = signal.butter(order, cutoff, 'high', fs=1000)
# Apply the filter
filtered_sig = signal.filtfilt(b, a, sig)
# Plot the results
plt.figure(figsize=(10, 6))
plt.plot(t, sig, 'b-', label='Noisy signal')
plt.plot(t, filtered_sig, 'r-', label='Filtered signal')
plt.legend()
plt.title('Butterworth High-Pass Filter')
plt.grid()
plt.show()You can see the output in the screenshot below.

This will remove frequency components below 15 Hz and keep those above it. When I used this on vibration data from a Chicago-based manufacturing facility, it helped isolate high-frequency machinery faults from the regular operating vibrations.
Check out Python SciPy Sparse
Method 3: Band-Pass Butterworth Filter
A band-pass filter allows frequencies within a certain range to pass through and attenuates frequencies outside that range:
# Design a band-pass Butterworth filter
order = 4
low_cutoff = 8 # Lower cutoff frequency
high_cutoff = 12 # Upper cutoff frequency
b, a = signal.butter(order, [low_cutoff, high_cutoff], 'bandpass', fs=1000)
# Apply the filter
filtered_sig = signal.filtfilt(b, a, sig)
# Plot the results
plt.figure(figsize=(10, 6))
plt.plot(t, sig, 'b-', label='Noisy signal')
plt.plot(t, filtered_sig, 'r-', label='Filtered signal')
plt.legend()
plt.title('Butterworth Band-Pass Filter')
plt.grid()
plt.show()You can see the output in the screenshot below.

In this example, I’m isolating frequencies between 8 Hz and 12 Hz. This is particularly useful when targeting specific frequency ranges, such as when I analyzed Chicago River water level data to identify specific tidal patterns.
Method 4: Band-Stop (Notch) Butterworth Filter
A band-stop filter (also called a notch filter) rejects frequencies within a specific range:
# Design a band-stop Butterworth filter
order = 4
low_cutoff = 18 # Lower cutoff frequency
high_cutoff = 22 # Upper cutoff frequency
b, a = signal.butter(order, [low_cutoff, high_cutoff], 'bandstop', fs=1000)
# Apply the filter
filtered_sig = signal.filtfilt(b, a, sig)
# Plot the results
plt.figure(figsize=(10, 6))
plt.plot(t, sig, 'b-', label='Noisy signal')
plt.plot(t, filtered_sig, 'r-', label='Filtered signal')
plt.legend()
plt.title('Butterworth Band-Stop Filter')
plt.grid()
plt.show()This is excellent for removing specific interference, like the 60 Hz power line noise I often encounter when working with sensor data from Chicago’s smart city infrastructure.
Check out Working with Python, Lil_Matrix SciPy
Method 5: Butterworth Filter for Image Processing
The Butterworth filter isn’t limited to 1D signals. It can be extended to 2D for image processing:
import numpy as np
from scipy import ndimage
import matplotlib.pyplot as plt
from matplotlib.colors import LogNorm
# Create a sample image with noise
size = 128
image = np.zeros((size, size))
image[32:96, 32:96] = 1 # Square in the middle
noise = np.random.normal(0, 0.1, (size, size))
noisy_image = image + noise
# Compute FFT of the image
fft_image = np.fft.fft2(noisy_image)
fft_shift = np.fft.fftshift(fft_image)
# Create Butterworth low-pass filter mask
rows, cols = noisy_image.shape
crow, ccol = rows//2, cols//2
D0 = 20 # Cutoff frequency
n = 2 # Order of the filter
u, v = np.meshgrid(np.arange(rows) - crow, np.arange(cols) - ccol, sparse=True)
D = np.sqrt(u**2 + v**2)
butterworth_mask = 1 / (1 + (D / D0)**(2*n))
# Apply filter to the FFT
fft_filtered = fft_shift * butterworth_mask
# Inverse FFT to get the filtered image
filtered_image = np.abs(np.fft.ifft2(np.fft.ifftshift(fft_filtered)))
# Display results
plt.figure(figsize=(12, 4))
plt.subplot(131)
plt.imshow(noisy_image, cmap='gray')
plt.title('Noisy Image')
plt.subplot(132)
plt.imshow(np.log1p(np.abs(fft_shift)), cmap='viridis')
plt.title('FFT (log scale)')
plt.subplot(133)
plt.imshow(filtered_image, cmap='gray')
plt.title('Filtered Image')
plt.tight_layout()
plt.show()I used this technique when processing satellite imagery of Chicago’s urban heat islands, which helped remove high-frequency noise while preserving important structural details.
Understand Filter Order and Cutoff Frequency
Two key parameters that affect Butterworth filter performance are:
- Order: Higher orders create sharper transitions between the passband and stopband but can introduce more ringing artifacts.
- Cutoff Frequency: This determines where the filter starts attenuating the signal.
Let’s visualize how these parameters affect the filter’s frequency response:
from scipy import signal
import numpy as np
import matplotlib.pyplot as plt
# Create frequency response plots for different orders
fs = 1000 # Sampling frequency
cutoff = 100 # Cutoff frequency
plt.figure(figsize=(10, 6))
for order in [1, 2, 4, 8]:
b, a = signal.butter(order, cutoff, 'low', fs=fs)
w, h = signal.freqz(b, a, worN=2000)
plt.plot((fs * 0.5 / np.pi) * w, abs(h), label=f"Order = {order}")
plt.xlabel('Frequency [Hz]')
plt.ylabel('Gain')
plt.title('Butterworth Filter Frequency Response for Different Orders')
plt.grid()
plt.legend()
plt.axvline(cutoff, color='k', linestyle='--', alpha=0.3, label='Cutoff Frequency')
plt.show()When working with Chicago traffic sensor data, I found that a 4th-order filter provided the best balance between effective noise removal and minimal signal distortion.
Real-World Applications
Butterworth filters have numerous practical applications:
- Biomedical Signal Processing: When I worked with ECG data from a Chicago hospital, Butterworth filters helped clean the signals for more accurate heart rate analysis.
- Audio Processing: They’re excellent for creating crossover filters in audio systems.
- Financial Time Series: I’ve used them to smooth stock price data while preserving important trends.
- Vibration Analysis: In predictive maintenance applications, they help isolate specific frequency bands of interest.
- Image Enhancement: As demonstrated earlier, they can remove noise from images while preserving important features.
Important Considerations When Using Butterworth Filters
Throughout my decade of experience with signal processing, I’ve learned a few important lessons about Butterworth filters:
- Edge Effects: The
filtfilt()function helps minimize edge effects by applying the filter in both forward and backward directions. - Filter Order: Start with lower orders (2-4) and increase if needed. Very high orders can cause numerical instability.
- Normalization: Always normalize your cutoff frequency to the Nyquist frequency or specify your sampling rate explicitly.
- Phase Distortion: Use
filtfilt()when phase preservation is important (it performs zero-phase filtering).
Python’s SciPy library makes implementing Butterworth filters straightforward, but understanding these nuances is key to effective application.
I hope you found this guide helpful for your signal processing tasks. Whether you’re cleaning environmental sensor data from Chicago’s lakefront or processing financial time series, the Butterworth filter is a versatile tool in your Python toolkit.
You may like to read:
- How to use Python SciPy Linprog
- Use Python SciPy Differential Evolution
- Python SciPy Ndimage Imread Tutorial

I am Bijay Kumar, a Microsoft MVP in SharePoint. Apart from SharePoint, I started working on Python, Machine learning, and artificial intelligence for the last 5 years. During this time I got expertise in various Python libraries also like Tkinter, Pandas, NumPy, Turtle, Django, Matplotlib, Tensorflow, Scipy, Scikit-Learn, etc… for various clients in the United States, Canada, the United Kingdom, Australia, New Zealand, etc. Check out my profile.