HOWTO · SciPy
SciPy signal.butter
Design and apply a Butterworth band-pass filter in Python with scipy.signal.butter, second-order sections, and a signal plot.
On this page
Use scipy.signal.butter to design a Butterworth filter, then apply the resulting second-order sections to a sampled signal. This example keeps a 50 Hz component while reducing a 250 Hz component, and plots the input beside the filtered result. It uses sosfiltfilt for forward-backward filtering, which avoids the phase shift of a single forward pass.
The function designs digital or analog Butterworth filters; it does not filter the data itself. The official SciPy signal-processing reference documents the filter-design functions.
Use scipy.signal.butter to design a Butterworth filter
A Butterworth design has a maximally flat magnitude response in its passband. The scipy.signal.butter function returns filter coefficients; for practical digital filtering, request second-order sections with output="sos". This representation is generally more numerically robust than a single numerator and denominator for higher-order filters.
The N argument is the filter order. For a band-pass design, the resulting transfer function has order 2 * N; the parameter N still specifies the order passed to the design function. The Wn argument gives the critical frequency, or a two-element low-to-high range for a band-pass filter. These are design frequencies, not separate passband and stopband specifications. A Butterworth filter has a gain of approximately 1/√2 (−3 dB) at each critical frequency.
Choose the filter type, order, and critical frequencies
Set btype="bandpass" to retain frequencies between two limits. For a low-pass filter use "lowpass"; for a high-pass filter use "highpass"; and for a band-stop filter use "bandstop". The limits must be positive, increasing, and below half the sampling rate.
When you pass fs, express Wn in the same units as fs (for example, both in hertz). Without fs, SciPy interprets digital critical frequencies in units where 1 is the Nyquist frequency, half the sampling rate. For example, a 100 Hz critical frequency at fs=1000 Hz corresponds to Wn=0.2 if fs is omitted.
Choose the order based on the transition sharpness and computational cost your application needs. A higher order makes the transition steeper, but increases computation and can make a design more sensitive to numerical effects. It does not guarantee a better result for every signal. For specifications that include passband ripple or stopband attenuation, use a design method that accepts those specifications, such as scipy.signal.iirdesign.
Apply the filter to a signal
This runnable example samples a signal at 1000 Hz. Its 50 Hz component is inside the 40–100 Hz passband, while its 250 Hz component is outside it. sosfiltfilt applies the sections forward and backward, so the result has no phase shift from a one-way causal filter. Install SciPy, NumPy, and Matplotlib in your Python environment before running the example. It saves the comparison figure as butterworth_filter.png in the current directory.
import matplotlib.pyplot as plt
import numpy as np
from scipy.signal import butter, sosfiltfilt
fs = 1000.0
duration = 1.0
t = np.arange(0.0, duration, 1.0 / fs)
signal = np.sin(2.0 * np.pi * 50.0 * t) + 0.6 * np.sin(2.0 * np.pi * 250.0 * t)
sos = butter(4, [40.0, 100.0], btype="bandpass", fs=fs, output="sos")
filtered = sosfiltfilt(sos, signal)
fig, axes = plt.subplots(2, 1, sharex=True, figsize=(9, 5))
axes[0].plot(t, signal, label="Input")
axes[0].set_ylabel("Amplitude")
axes[0].legend()
axes[1].plot(t, filtered, label="Filtered", color="tab:orange")
axes[1].set_xlabel("Time (s)")
axes[1].set_ylabel("Amplitude")
axes[1].legend()
fig.tight_layout()
fig.savefig("butterworth_filter.png", dpi=150)
plt.close(fig)
The input is the sum of two known sinusoids, so the example is repeatable and does not depend on random noise. To use your own samples, replace signal and set fs to their sampling rate; keep the signal array and filter settings in matching units. For streaming or real-time processing, use a one-way filter such as sosfilt and account for its phase and startup behavior.
Plot and interpret the filtered signal
Open butterworth_filter.png to compare the combined input in the first axes with the filtered samples in the second axes over the same time range. The 50 Hz oscillation remains prominent, while the 250 Hz component is attenuated. The plot is a qualitative comparison rather than a promise of exact amplitudes: the response near each critical frequency is gradual, and the result also depends on the data and filter order.
The plotted example uses forward-backward filtering, which processes the data in both directions. This removes phase delay but makes the operation non-causal, so it is suitable for offline data rather than a live stream. Forward-backward filtering can also produce transients near the beginning and end of a finite record. Inspect those regions before interpreting measurements there. For live data, use a causal filter and account for its delay and initial-state behavior instead.
Common pitfalls: sampling rate, filter order, and transients
- Sampling rate:
fsmust describe the data that you actually sampled. A mismatch changes the physical meaning of every frequency in the design. Frequencies at or above the Nyquist frequency cannot be represented reliably in sampled data; use an appropriate acquisition rate and anti-aliasing before sampling. - Filter order:
Nis not a strength score. Increasing it steepens the transition and raises computational and numerical costs. Compare the resulting response with your task’s requirements instead of assuming that the largest practical order is best. - Frequency arguments: With
fssupplied, use the same units forWnandfs. Withoutfs, normalize digital frequencies to the Nyquist frequency. A band-passWncontains its lower and upper critical frequencies; it does not encode stopband edges or attenuation targets. - Transients and direction:
sosfiltfiltis a convenient offline zero-phase operation, whilesosfiltis a causal one-way operation suited to streaming. Both can show startup or edge effects, so leave enough data around the region you intend to analyze and check the endpoints.