Search code examples
pythonnumpyvectorminimum

A way to get the local minima and maxima of a vector


I've been trying to retrieve the extrema of a vector that looks like this :

[![First case][1]][1]
(source: noelshack.com)

or like this : [![Second case][2]][2]
(source: noelshack.com)

I've been trying to retrieve local maxima and minima, it works well with : (diff(sign(diff(values_right_vector))) > 0).nonzero()[0] + 1 but afterwards it is only workaround and workaround because there is always a case where my previous workaround fails..

It has always this same pattern.

Do you have any ideas how can I retrieve those maxima and minima no matter the input vector (left and right) on the image.

Here is a sample :

[-2.7, -2.5, -2.1, -2.1, -1.8, -1.4, -0.9, -0.2, 0.5, 1.4, 2.2, 2.9, 3.5, 3.8, 3.8, 3.3, 2.3, 1.1, -0.5, -2.1, -3.5, -4.7, -5.5, -5.8, -5.6, -5.0, -4.2, -3.3, -2.3, -1.4, -0.8, -0.3, 0.0, 0.2, 0.2, 0.2, 0.1, 0.0, 0.0, 0.0, -0.1, -0.1, -0.1, -0.1, -0.1, -0.1, -0.2, -0.2, -0.2, -0.2, -0.2, -0.2, -0.2, -0.1, -0.1, -0.1, -0.1, -0.1, -0.2, -0.3, -0.4, -0.4, -0.5, -0.4, -0.3, -0.1, 0.2, 0.5, 0.7, 0.9, 0.9, 1.0, 0.9, 0.9, 0.9, 0.8, 0.7, 0.6, 0.3, 0.0, -0.4, -0.9, -1.3, -1.5, -1.6, -1.5, -1.1, -0.5, 0.2, 1.2, 2.1, 3.0, 3.8, 4.3, 4.3, 4.0, 3.2, 1.9, 0.4, -1.3, -3.0, -4.4, -5.4, -6.0, -6.0, -5.6, -4.8, -3.9, -2.9, -1.9, -1.2, -0.6, -0.2, 0.0, 0.1, 0.1, 0.1, 0.0, 0.0, -0.1, -0.1, -0.1, -0.1, 0.0, 0.0, 0.0, 0.0, 0.0, -0.1, -0.1, -0.1, -0.2, -0.2, -0.2, -0.2, -0.1, -0.1, 0.0, 0.0, 0.0, 0.0, -0.1, -0.3, -0.5, -0.7, -0.9, -1.1, -1.1, -1.0, -0.8, -0.4, 0.3, 1.1, 1.9, 2.8, 3.6, 4.2, 4.5, 4.5, 4.1, 3.4, 2.5, 1.5, 0.5, -0.5, -1.4, -2.1, -2.8, -3.3, -3.7, -3.9, -3.9, -3.8, -3.4, -2.9, -2.2, -1.3, -0.4, 0.7, 1.7, 2.5, 3.2, 3.6, 3.6, 3.2, 2.4, 1.3, -0.1, -1.6, -3.0, -4.1, -4.9, -5.1, -5.0, -4.4, -3.6, -2.7, -1.8, -1.1, -0.5, -0.1, 0.1, 0.2, 0.2, 0.1, 0.1, 0.0, -0.1, -0.1]```

 [1]: https://i.sstatic.net/O3er1.png
 [2]: https://i.sstatic.net/cbqNK.png

Solution

  • Scipy has a find_peaks function that you can manipulate to find the peaks you want using the distance parameter. The distance parameter tells Scipy how many spaces between samples it should look for peaks. You can adjust this to fit your data best. Using just the sample data you provided,

    import numpy as np
    from scipy.signal import find_peaks
    import matplotlib.pyplot as plt
    
    y = np.array([-2.7, -2.5, -2.1, -2.1, -1.8, -1.4, -0.9, -0.2, 0.5, 1.4, 2.2, 2.9, 3.5, 3.8, 3.8, 3.3, 2.3, 1.1, -0.5, -2.1, -3.5, -4.7, -5.5, -5.8, -5.6, -5.0, -4.2, -3.3, -2.3, -1.4, -0.8, -0.3, 0.0, 0.2, 0.2, 0.2, 0.1, 0.0, 0.0, 0.0, -0.1, -0.1, -0.1, -0.1, -0.1, -0.1, -0.2, -0.2, -0.2, -0.2, -0.2, -0.2, -0.2, -0.1, -0.1, -0.1, -0.1, -0.1, -0.2, -0.3, -0.4, -0.4, -0.5, -0.4, -0.3, -0.1, 0.2, 0.5, 0.7, 0.9, 0.9, 1.0, 0.9, 0.9, 0.9, 0.8, 0.7, 0.6, 0.3, 0.0, -0.4, -0.9, -1.3, -1.5, -1.6, -1.5, -1.1, -0.5, 0.2, 1.2, 2.1, 3.0, 3.8, 4.3, 4.3, 4.0, 3.2, 1.9, 0.4, -1.3, -3.0, -4.4, -5.4, -6.0, -6.0, -5.6, -4.8, -3.9, -2.9, -1.9, -1.2, -0.6, -0.2, 0.0, 0.1, 0.1, 0.1, 0.0, 0.0, -0.1, -0.1, -0.1, -0.1, 0.0, 0.0, 0.0, 0.0, 0.0, -0.1, -0.1, -0.1, -0.2, -0.2, -0.2, -0.2, -0.1, -0.1, 0.0, 0.0, 0.0, 0.0, -0.1, -0.3, -0.5, -0.7, -0.9, -1.1, -1.1, -1.0, -0.8, -0.4, 0.3, 1.1, 1.9, 2.8, 3.6, 4.2, 4.5, 4.5, 4.1, 3.4, 2.5, 1.5, 0.5, -0.5, -1.4, -2.1, -2.8, -3.3, -3.7, -3.9, -3.9, -3.8, -3.4, -2.9, -2.2, -1.3, -0.4, 0.7, 1.7, 2.5, 3.2, 3.6, 3.6, 3.2, 2.4, 1.3, -0.1, -1.6, -3.0, -4.1, -4.9, -5.1, -5.0, -4.4, -3.6, -2.7, -1.8, -1.1, -0.5, -0.1, 0.1, 0.2, 0.2, 0.1, 0.1, 0.0, -0.1, -0.1])
    
    # Get the maxima and minima
    maxima, _ = find_peaks(y, distance = 50)
    minima, _ = find_peaks(-y, distance = 50)
    

    find_peaks returns the indices of the peaks, which is why we can use -y to get the minima.

    You can also index the maxima and minima to select the peaks you want, by doing something like maxima[::2] to select every other maxima.

    fig, ax = plt.subplots()
    
    ax.plot(y)
    ax.plot(maxima, y[maxima], 'x')
    ax.plot(minima, y[minima], 'x')
    plt.show()
    

    enter image description here