MATLAB findpeaks to Python: scipy.signal.find_peaks

Convert MATLAB findpeaks to Python with scipy.signal.find_peaks. Maps MinPeakHeight, MinPeakDistance, and prominence, plus the 0-based location gotcha and when results differ from MATLAB.

The direct replacement: scipy.signal.find_peaks

MATLAB's findpeaks lives in the Signal Processing Toolbox. The Python equivalent is scipy.signal.find_peaks, added in SciPy 1.1.0 — it finds local maxima by the same neighbor-comparison approach, so for well-separated peaks the two agree.

The one thing to internalize up front: the two functions return different things. MATLAB returns *values then locations*; SciPy returns *indices then a properties dict*.

`matlab % MATLAB x = [0 2 0 4 0 6 0 3 0]; [pks, locs] = findpeaks(x); % pks = [2 4 6 3] (peak values) % locs = [2 4 6 8] (1-based positions) `

`python # Python — scipy.signal.find_peaks import numpy as np import scipy.signal as signal

x = np.array([0, 2, 0, 4, 0, 6, 0, 3, 0]) locs = signal.find_peaks(x)[0] # [1 3 5 7] — 0-based indices pks = x[locs] # [2 4 6 3] — values `

find_peaks returns a tuple (peaks, properties). The first element is an array of 0-based indices, not the peak values — so you take x[locs] to get the values. That is exactly what a faithful conversion produces.

The locations are 0-based — the #1 gotcha

MATLAB's locs are 1-based positions; SciPy's are 0-based indices. In the example above, MATLAB reports the peak at 6 as position 6, while SciPy reports it as index 5.

This matters only depending on how you *use* the result:

- Indexing back into the signalpks = x(locs)pks = x[locs] — is correct as-is. A 0-based index used to subscript a 0-based array gives the right value. - Reporting or plotting the positionfprintf('peak at %d', locs(1)) — is off by one. MATLAB prints 2, Python prints 1 for the same peak.

If you need MATLAB's 1-based numbers (for a table, a label, or time alignment), add one: positions = locs + 1. If you are only slicing the array, leave them as they are.

Name/Value options become keyword arguments

MATLAB's 'Name', value pairs map cleanly to SciPy keyword arguments:

| MATLAB | scipy.signal.find_peaks | |---|---| | 'MinPeakHeight', h | height=h | | 'MinPeakDistance', d | distance=d | | 'MinPeakProminence', p | prominence=p | | 'MinPeakWidth', w | width=w | | 'Threshold', t | threshold=t |

`matlab % MATLAB [tall, tallLoc] = findpeaks(x, 'MinPeakHeight', 3, 'MinPeakDistance', 2); `

`python # Python tallLoc = signal.find_peaks(x, height=3, distance=2)[0] tall = x[tallLoc] `

One subtlety: distance in SciPy must be an integer number of samples ≥ 1. MATLAB's MinPeakDistance can be given in the signal's x-units when you pass an x vector — convert it to a sample count first if your spacing isn't 1.

Getting widths and prominences

MATLAB returns extra outputs from the same call:

`matlab % MATLAB — [pks, locs, widths, proms] [pks, locs, w, p] = findpeaks(x, 'Annotate', 'extents'); `

In SciPy those live in the properties dict (the second return value), and you must *request* them by passing the matching keyword — otherwise they aren't computed:

`python # Python — widths/prominences come back in the properties dict peaks, props = signal.find_peaks(x, prominence=0, width=0) widths = props['widths'] prominences = props['prominences'] left_bases = props['left_bases'] right_bases = props['right_bases'] `

Passing prominence=0/width=0 means "no minimum, but compute the value for every peak." It's a common surprise that props['widths'] is missing until you pass width=....

When the results will not match MATLAB exactly

For clean, well-separated peaks the two functions agree. They can diverge on dense or noisy signals — worth knowing before you trust a one-to-one port:

- Distance tie-breaking. Both remove smaller peaks within distance, but the order of elimination differs. MATLAB starts from the tallest peak; SciPy removes lowest-priority peaks iteratively. On clusters of similar-height peaks the surviving set can differ. - Width is measured differently by default. MATLAB's width defaults to the half-prominence reference; SciPy's width is measured at rel_height=0.5 of prominence too, but the interpolation at the edges can shift values slightly. - Endpoints. Neither reports the first or last sample as a peak, so a monotonic ramp yields no peaks in both — good news for parity.

If exact MATLAB parity matters (e.g., reproducing a paper), filter the signal first and verify a handful of peaks by hand. For most migrations — detect peaks, grab their values — find_peaks is a drop-in.

Convert your findpeaks code automatically

The examples above are exactly what the [MATLABtoPython converter](/convert) produces — it maps findpeaks to signal.find_peaks, rewrites the 'Name', value options to keyword arguments, adds pks = x[locs] for the values, and flags the 0-based-location difference so you don't get caught by it.

Paste your MATLAB signal-processing code into the [converter](/convert) and get runnable, scipy-backed Python in seconds — fft, butter, filter, and findpeaks all map to their SciPy equivalents.

Start converting

Free for 50 lines. No account required.

More like this, once a week

New articles on MATLAB-to-Python migration. Short, practical, no fluff — the same tone as the one you just read.

Keep reading