0

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.stack.imgur.com/O3er1.png
 [2]: https://i.stack.imgur.com/cbqNK.png
Glorfindel
  • 21,988
  • 13
  • 81
  • 109
AAAAAAA
  • 17
  • 4
  • Possible duplicate of [Finding local maxima/minima with Numpy in a 1D numpy array](https://stackoverflow.com/questions/4624970/finding-local-maxima-minima-with-numpy-in-a-1d-numpy-array) – m13op22 Jun 20 '19 at 20:50
  • No this is not a duplicate, as I explained after retrieving those extrema points, I need to parse them and it is only workaround after workaround.. – AAAAAAA Jun 20 '19 at 20:58
  • Can you provide some sample data that exhibits this pattern? – m13op22 Jun 20 '19 at 21:01
  • Also, what explicitly is your criteria for choosing extrema? – m13op22 Jun 20 '19 at 21:07
  • The criteria is the extrema showed in green. For the maxima it is pretty simple, just take maxima via the formula I gave and threshold all "false" maxima below the `global max - threshold` For the minima it is always the second "drop" after the maxima (but for the second picture there is a trick because it starts with only the first one..) – AAAAAAA Jun 20 '19 at 21:19
  • post up some sample data if you want careful help – kevinkayaks Jun 20 '19 at 21:37
  • done :) I don't think it is solvable with a general deterministic algorithm.. There is too much cases. The second one above proves it.. If you have an idea don't hesitate ! – AAAAAAA Jun 20 '19 at 21:47

1 Answers1

0

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

m13op22
  • 2,168
  • 2
  • 16
  • 35