Finding singulars/sets of local maxima/minima in a 1D-NumPy array (once again)

  • A+

I would like to have a function that can detect where the local maxima/minima are in an array (even if there is a set of local maxima/minima). Example:

Given the array

test03 = np.array([2,2,10,4,4,4,5,6,7,2,6,5,5,7,7,1,1]) 

I would like to have an output like:

set of 2 local minima => array[0]:array[1] set of 3 local minima => array[3]:array[5] local minima, i = 9 set of 2 local minima => array[11]:array[12] set of 2 local minima => array[15]:array[16] 

As you can see from the example, not only are the singular values detected but, also, sets of local maxima/minima.

I know in this question there are a lot of good answers and ideas, but none of them do the job described: some of them simply ignore the extreme points of the array and all ignore the sets of local minima/maxima.

Before asking the question, I wrote a function by myself (a newbie implementation, I am sorry) that does exactly what I described above (the function is at the end of this question).

I am also sure that is NOT the best way to work with Python. Are there builtin functions, APIs, libraries, etc. that I can use? Any other function suggestion? A one-line instruction?

def local_min(a):     candidate_min=0     for i in range(len(a)):          # Controlling the first left element         if i==0 and len(a)>=1:             # If the first element is a singular local minima             if a[0]<a[1]:                 print("local minima, i = 0")             # If the element is a candidate to be part of a set of local minima             elif a[0]==a[1]:                 candidate_min=1         # Controlling the last right element         if i == (len(a)-1) and len(a)>=1:             if candidate_min > 0:                 if a[len(a)-1]==a[len(a)-2]:                     print("set of " + str(candidate_min+1)+ " local minima => array["+str(i-candidate_min)+"]:array["+str(i)+"]")             if a[len(a)-1]<a[len(a)-2]:                 print("local minima, i = " + str(len(a)-1))         # Controlling the other values in the middle of the array         if i>0 and i<len(a)-1 and len(a)>2:             # If a singular local minima             if (a[i]<a[i-1] and a[i]<a[i+1]):                 print("local minima, i = " + str(i))                 # print(str(a[i-1])+" > " + str(a[i]) + " < "+str(a[i+1])) #debug             # If it was found a set of candidate local minima             if candidate_min >0:                 # The candidate set IS a set of local minima                 if a[i] < a[i+1]:                     print("set of " + str(candidate_min+1)+ " local minima => array["+str(i-candidate_min)+"]:array["+str(i)+"]")                     candidate_min = 0                 # The candidate set IS NOT a set of local minima                 elif a[i] > a[i+1]:                     candidate_min = 0                 # The set of local minima is growing                 elif a[i] == a[i+1]:                     candidate_min = candidate_min + 1                 # It never should arrive in the last else                 else:                     print("Something strange happen")                     return -1             # If there is a set of candidate local minima (first value found)             if (a[i]<a[i-1] and a[i]==a[i+1]):                 candidate_min = candidate_min + 1 

Note: I tried to enrich the code with some comment to understand what I would like to do. I know that the function that I propose is not clean and just prints the results that can be stored and returned at the end. It was written to give an example. The algorithm I propose should be O(n).


Somebody was suggesting to import from scipy.signal import argrelextrema and use the function like:

def local_min_scipy(a):     minima = argrelextrema(a, np.less_equal)[0]     return minima  def local_max_scipy(a):     minima = argrelextrema(a, np.greater_equal)[0]     return minima 

To have something like that is what I am really looking for. However, it doesn't work properly when the sets of local minima/maxima have more than two values. For example:

test03 = np.array([2,2,10,4,4,4,5,6,7,2,6,5,5,7,7,1,1])  print(local_max_scipy(test03)) 

The output is:

[ 0  2  4  8 10 13 14 16] 

Of course in test03[4] I have a minimum and not a maximum. How do I fix this behavior? (I don't know if this is another question or if this is the right place where to ask it.)


A full vectored solution:

test03 = np.array([2,2,10,4,4,4,5,6,7,2,6,5,5,7,7,1,1])  # Size 17 extended = np.empty(len(test03)+2)  # Rooms to manage edges, size 19 extended[1:-1] = test03 extended[0] = extended[-1] = np.inf  flag_left = extended[:-1] <= extended[1:]  # Less than successor, size 18 flag_right = extended[1:] <= extended[:-1]  # Less than predecessor, size 18  flagmini = flag_left[1:] & flag_right[:-1]  # Local minimum, size 17 mini = np.where(flagmini)[0]  # Indices of minimums spl = np.where(np.diff(mini)>1)[0]+1  # Places to split result = np.split(mini, spl) 


[0, 1] [3, 4, 5] [9] [11, 12] [15, 16] 


:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: