15

I have two arrays x and y as :

x = np.array([6, 3, 5, 2, 1, 4, 9, 7, 8])
y = np.array([2, 1, 3, 5, 3, 9, 8, 10, 7])

I am finding index of local minima and maxima as follows:

sortId = np.argsort(x)
x = x[sortId]
y = y[sortId]
minm = np.array([])
maxm = np.array([])
while i < y.size-1:
   while(y[i+1] >= y[i]):
      i = i + 1

   maxm = np.insert(maxm, 0, i)
   i++
   while(y[i+1] <= y[i]):
      i = i + 1

   minm = np.insert(minm, 0, i)
   i++

What is the problem in this code? The answer should be index of minima = [2, 5, 7] and that of maxima = [1, 3, 6].

Cleb
  • 25,102
  • 20
  • 116
  • 151
prtkp
  • 537
  • 1
  • 6
  • 8
  • 1
    whate are you doing with `sortId=np.argsort(x); x=x[sortId]`? – fferri Jun 26 '15 at 10:23
  • This solution with a double `while` loop will be very slow (and I'm not even talking about `np.insert` that pretty much reallocates the `minm`/`nmax` arrays at every iteration). See https://stackoverflow.com/questions/4624970/finding-local-maxima-minima-with-numpy-in-a-1d-numpy-array for a way of properly doing this in numpy. – rth Jun 26 '15 at 10:38
  • @mescalinum: I am sorting x values and corresponding indices will be stored in sortId. so that I can arrange y values in that order. – prtkp Jun 26 '15 at 11:05
  • @Cleb : i want peaks and troughs. – prtkp Jun 26 '15 at 11:07
  • @rth : Actually in my data set, points are very adjascent, so the method mentioned in ur link is also giving some extra points around maxima and minima. – prtkp Jun 26 '15 at 11:11
  • @Cleb : wen u will plot the graph with these x and y values, you'll get peaks at x[1],x[2],x[3] and troughs at x[2],x[5],x[7]. these are the indices of peaks and troughs – prtkp Jun 26 '15 at 11:36
  • @Cleb : absolutely sir ... dats why i was saying the index of maxima are 1,3,6 and the points are (2,5), (4,9) and (7,10)...similarly index of minima are 2,5,7 nd points are (3,1), (6,2) and (8,7). – prtkp Jun 26 '15 at 11:58
  • @Cleb : could u find the error in this code or can u provide a new code for this.....the problem is I hav a large data set with x and y values which are very adjascent so finding exact peaks are difficult. – prtkp Jun 26 '15 at 12:29
  • @prtkp: Not sure what goes wrong in the while loop but I added an alternative solution below which should be much more efficient then the for loop and which returns the desired output. Let me know whether that suits you or whether it has to be revised! – Cleb Jun 28 '15 at 10:40

3 Answers3

27

You do not need this while loop at all. The code below will give you the output you want; it finds all local minima and all local maxima and stores them in minm and maxm, respectively. Please note: When you apply this to large datasets, make sure to smooth the signals first; otherwise you will end up with tons of extrema.

import numpy as np
from scipy.signal import argrelextrema
import matplotlib.pyplot as plt

x = np.array([6, 3, 5, 2, 1, 4, 9, 7, 8])
y = np.array([2, 1, 3 ,5 ,3 ,9 ,8, 10, 7])

# sort the data in x and rearrange y accordingly
sortId = np.argsort(x)
x = x[sortId]
y = y[sortId]

# this way the x-axis corresponds to the index of x
plt.plot(x-1, y)
plt.show()
maxm = argrelextrema(y, np.greater)  # (array([1, 3, 6]),)
minm = argrelextrema(y, np.less)  # (array([2, 5, 7]),)

This should be far more efficient than the above while loop.

The plot looks like this; I shifted the x-values so that they correspond to the returned indices in minm and maxm):

enter image description here

As of SciPy version 1.1, you can also use find_peaks:

from scipy.signal import find_peaks

peaks, _ = find_peaks(y)

# this way the x-axis corresponds to the index of x
plt.plot(x-1, y)
plt.plot(peaks, y[peaks], "x")
plt.show()

That yields

enter image description here

The nice thing is, that you can now also easily also set a minimum peak height (e.g. 8):

peaks, _ = find_peaks(y, height=8)

# this way the x-axis corresponds to the index of x
plt.plot(x-1, y)
plt.plot(peaks, y[peaks], "x")
plt.show() 

enter image description here

Note that now the first peak is excluded as its height is below 8.

Furthermore, you can set also the minimal distance between peaks (e.g. 5):

peaks, _ = find_peaks(y, distance=5)

# this way the x-axis corresponds to the index of x
plt.plot(x-1, y)
plt.plot(peaks, y[peaks], "x")
plt.show()

enter image description here

Now the middle peak is excluded as its distance to the other two peaks is less than 5.

Cleb
  • 25,102
  • 20
  • 116
  • 151
  • ya....this is working fine...my solution given below is also giving the same answer....but the problem is coz of large data set, its giving lots of extremas as u told in my second question and suggested to smooth the signal before finding extremas...btw Thanks – prtkp Jun 29 '15 at 05:09
  • 1
    Sorry to add this as a comment, but not sure how to word the question when searching. What do you mean by "smooth the signals"? What would you use for that? Thanks. – datahappy Feb 16 '16 at 14:01
  • 1
    @datahappy: You could search for "smooth signal scipy" and also "running average" which should get you on the right track. Smoothing gets rid of a lot of local extrema which helps to find the actual peaks. – Cleb Feb 16 '16 at 14:06
  • 6
    Hi, It is possiblt toget minima by using `peaks, _ = find_peaks(-y)`. – Echan Nov 12 '19 at 15:09
1
x=np.array([6,3,5,2,1,4,9,7,8])
y=np.array([2,1,3,5,7,9,8,10,7])

sort_idx = np.argsort(x)
y=y[sort_idx]
x=x[sort_idx]
minm=np.array([],dtype=int)
maxm=np.array([],dtype=int)
length = y.size
i=0

while i < length-1:
    if i < length - 1:
        while i < length-1 and y[i+1] >= y[i]:
            i+=1

        if i != 0 and i < length-1:
            maxm = np.append(maxm,i)

        i+=1

    if i < length - 1:
        while i < length-1 and y[i+1] <= y[i]:
            i+=1

        if i < length-1:
            minm = np.append(minm,i)
        i+=1


print minm
print maxm

minm and maxm contain indices of minima and maxima, respectively.

Cleb
  • 25,102
  • 20
  • 116
  • 151
prtkp
  • 537
  • 1
  • 6
  • 8
0

This will work fine.

Python uses += instead of ++.

Before you use i in a while loop you have to assign some value - in this case 0 - , this way initializing it to avoid error.

import numpy as np

x=np.array([6,3,5,2,1,4,9,7,8])
y=np.array([2,1,3,5,3,9,8,10,7])


sortId=np.argsort(x)
x=x[sortId]
y=y[sortId]
minm = np.array([])
maxm = np.array([])
i = 0
while i < y.size-1:
   while(y[i+1] >= y[i]):
      i+=1

   maxm=np.insert(maxm,0,i)
   i+=1
   while(y[i+1] <= y[i]):
      i+=1

   minm=np.insert(minm,0,i)
   i+=1

print minm, maxm
Cleb
  • 25,102
  • 20
  • 116
  • 151
Geeocode
  • 5,705
  • 3
  • 20
  • 34
  • resulting in [ 7. 5. 2.] [ 6. 3. 1.] – Geeocode Jun 26 '15 at 10:55
  • Did you manage to run it? Ignoring the original technical issue as while loop and reallocates could someone run my solution to check it doesn't gives "index is out of bound" error? – Geeocode Jun 26 '15 at 11:16
  • wen i am executing, the error is coming. wen u are executing 3rd while loop , you are not checking the index bound. Take another example and then try to execute – prtkp Jun 26 '15 at 11:38
  • the script reach the 3rd while loop of course and the python parser would give me an error message if it were but it is NOT. Pls. send me the full code that you run to check it. Something is wrong by your side. – Geeocode Jun 26 '15 at 12:00