Written and maintained by CASRAI Editorial Board
Last updated
SciPy signals work lives in one module, scipy.signal. It holds the functions researchers use to filter a recording, find peaks in it, and estimate its power spectrum. This guide maps each of those tasks to the function that does it, then walks through one complete example. If you searched “scipy signal” and “scipy signals”, this is the same module.
What scipy.signal is used for in research
The SciPy reference page for scipy.signal groups the module into these areas: convolution, B-splines, filtering, filter design (including Matlab-style IIR design), waveforms, window functions, peak finding, spectral analysis, and chirp Z-transform and zoom FFT. Most tutorials cover only filtering. The table maps a research task to the function and its signature as listed in the reference.
| Task | Function and signature |
|---|---|
| Design a Butterworth filter | butter(N, Wn[, btype, analog, output, fs]) |
| Apply a filter, one pass | sosfilt(sos, x[, axis, zi]) or lfilter(b, a, x[, axis, zi]) |
| Apply a filter forward and backward | sosfiltfilt(sos, x[, axis, padtype, padlen]) or filtfilt(b, a, x[, axis, padtype, padlen, ...]) |
| Smooth with a polynomial window | savgol_filter(x, window_length, polyorder[, ...]) |
| Median filter | medfilt(volume[, kernel_size]) |
| Remove a trend | detrend(data[, axis, type, bp, overwrite_data]) |
| Convolve or correlate two signals | convolve(in1, in2[, mode, method]), correlate(in1, in2[, mode, method]) |
| Analytic signal | hilbert(x[, N, axis]) |
| Resample to a new number of samples | resample(x, num[, t, axis, window, domain]) |
| Find peaks | find_peaks(x[, height, threshold, distance, ...]) |
| Power spectrum (Welch) | welch(x[, fs, window, nperseg, noverlap, ...]) |
The conceptual background is in what is signal processing. For a plain FFT with NumPy, see NumPy Fourier transform.
Install or access
SciPy’s install page (scipy.org/install) gives these commands:
python -m pip install scipyconda install scipy(ormamba install scipy)
The page says the recommended method depends on your workflow, and lists project-based tools (uv, pixi) as the recommended route for new users. Then import the module with from scipy import signal.
Step-by-step: design and apply a Butterworth filter, then find peaks and a Welch spectrum
The filter call follows the example in the scipy.signal.butter documentation: a 10th-order high-pass at 15 Hz on a signal sampled at 1000 Hz, which removes a 10 Hz tone. Here sig is your one-dimensional array of samples.
- Import the module:
from scipy import signal - Design the filter as second-order sections:
sos = signal.butter(10, 15, 'hp', fs=1000, output='sos') - Apply it forward and backward:
y = signal.sosfiltfilt(sos, sig) - Find peaks:
peaks, props = signal.find_peaks(y, height=0, distance=50, prominence=0.5). The values 0, 50 and 0.5 are placeholders; set them for your data. - Estimate the power spectrum:
f, Pxx = signal.welch(y, fs=1000)
Passing fs to butter means the cutoff Wn is in the same units as fs (Hz here). Without fs, Wn is normalised so that 1 is the Nyquist frequency.
Output and how to read it
butter and the sos array
Wn is the -3 dB point. It is a scalar for lowpass and highpass and a length-2 sequence for bandpass and bandstop. With output='sos' the result is an array of shape (n_sections, 6), which sosfilt and sosfiltfilt take as their first argument.
sosfiltfilt versus sosfilt
sosfiltfilt filters forward and backward and returns an array of the same shape as x. In the SciPy docs example its output keeps the phase of the input, while single-pass sosfilt shows significant phase delay. If peak timing matters, that difference matters.
find_peaks
It returns (peaks, properties). peaks holds sample indices, not times: divide by the sampling rate to get seconds. A peak is a sample whose two direct neighbours are smaller; for a flat peak the middle index is returned (rounded down when the number of samples is even). distance is in samples (at least 1) and removes smaller peaks first. The conditions are evaluated in this order: plateau_size, height, threshold, distance, prominence, width. The properties dictionary can contain keys such as peak_heights, prominences, left_bases, right_bases, widths, width_heights, left_ips and right_ips, depending on which conditions you set.
welch
It returns f (frequencies) and Pxx (power). With the default scaling='density' the units are V2/Hz when the input is in V and fs in Hz; scaling='spectrum' gives V2. For a string or tuple window, nperseg defaults to 256 and noverlap defaults to nperseg//2. The default window is 'hann_periodic' and the default detrend is 'constant'.
Troubleshooting
Unstable or inaccurate filter
The butter default output is 'ba', but the documentation says: “Default is ‘ba’ for backwards compatibility, but ‘sos’ should be used for general-purpose filtering.” High-order or narrowband ba filters can be unstable or inaccurate because of floating-point precision. Pass output='sos' and use sosfilt or sosfiltfilt.
Wrong cutoff frequency
Check whether you passed fs. Without it, Wn is a fraction of the Nyquist frequency (the docs define it as 2 times the critical frequency divided by fs); with it, Wn is in Hz. The fs argument was added in SciPy 1.2.0.
Start and end of the output look odd
sosfiltfilt pads the signal at both ends (padtype='odd', with padlen set by default from the number of sections). You can change both arguments. sosfiltfilt was added in SciPy 0.18.0.
find_peaks returns too many peaks or odd results
On a noisy signal, smooth first or use find_peaks_cwt, as the documentation advises. NaNs in the input give unexpected results.
Legacy functions
The reference marks spectrogram as a legacy function, along with stft, istft, check_COLA and sosfreqz. Check the current reference before starting new code with them.
Free alternatives
| Need | Option | Note |
|---|---|---|
| A frequency spectrum from the FFT only | NumPy’s FFT routines | Covered in NumPy Fourier transform. |
| Peaks in a noisy signal | find_peaks_cwt in scipy.signal |
Named in the find_peaks documentation for noisy signals. |
For other SciPy tasks, see SciPy interpolate, curve fitting with SciPy, and, for a related smoothing method in another tool, moving average in MATLAB.
FAQ
What is the difference between scipy signal and scipy.signal?
None. “SciPy signal” and “scipy signals” are informal names for the scipy.signal module, imported with from scipy import signal.
Should I use lfilter or sosfilt?
For general-purpose filtering the butter documentation recommends second-order sections (output='sos') with sosfilt, because ba coefficients can lose accuracy at high order or narrow bandwidth.
How do I filter without shifting peaks in time?
Use a forward-backward filter such as sosfiltfilt or filtfilt. The SciPy example shows its output matching the input phase, where sosfilt adds phase delay.
Are peak positions in seconds?
No. find_peaks returns sample indices; convert them using your sampling rate.
Sources
- SciPy reference,
scipy.signal: docs.scipy.org/doc/scipy/reference/signal.html (checked 2026-10-29) - SciPy reference,
scipy.signal.butter: docs.scipy.org/doc/scipy/reference/generated/scipy.signal.butter.html (checked 2026-10-29) - SciPy reference,
scipy.signal.sosfiltfilt: docs.scipy.org/doc/scipy/reference/generated/scipy.signal.sosfiltfilt.html (checked 2026-10-29) - SciPy reference,
scipy.signal.find_peaks: docs.scipy.org/doc/scipy/reference/generated/scipy.signal.find_peaks.html (checked 2026-10-29) - SciPy reference,
scipy.signal.welch: docs.scipy.org/doc/scipy/reference/generated/scipy.signal.welch.html (checked 2026-10-29) - SciPy installation: scipy.org/install (checked 2026-10-29)







