Finding local maxima/minima with Numpy in a 1D numpy array

后端 未结 12 1726
迷失自我
迷失自我 2020-11-22 15:13

Can you suggest a module function from numpy/scipy that can find local maxima/minima in a 1D numpy array? Obviously the simplest approach ever is to have a look at the neare

相关标签:
12条回答
  • 2020-11-22 15:24

    While this question is really old. I believe there is a much simpler approach in numpy (a one liner).

    import numpy as np
    
    list = [1,3,9,5,2,5,6,9,7]
    
    np.diff(np.sign(np.diff(list))) #the one liner
    
    #output
    array([ 0, -2,  0,  2,  0,  0, -2])
    

    To find a local max or min we essentially want to find when the difference between the values in the list (3-1, 9-3...) changes from positive to negative (max) or negative to positive (min). Therefore, first we find the difference. Then we find the sign, and then we find the changes in sign by taking the difference again. (Sort of like a first and second derivative in calculus, only we have discrete data and don't have a continuous function.)

    The output in my example does not contain the extrema (the first and last values in the list). Also, just like calculus, if the second derivative is negative, you have max, and if it is positive you have a min.

    Thus we have the following matchup:

    [1,  3,  9,  5,  2,  5,  6,  9,  7]
        [0, -2,  0,  2,  0,  0, -2]
            Max     Min         Max
    
    0 讨论(0)
  • 2020-11-22 15:24

    Another one:

    
    def local_maxima_mask(vec):
        """
        Get a mask of all points in vec which are local maxima
        :param vec: A real-valued vector
        :return: A boolean mask of the same size where True elements correspond to maxima. 
        """
        mask = np.zeros(vec.shape, dtype=np.bool)
        greater_than_the_last = np.diff(vec)>0  # N-1
        mask[1:] = greater_than_the_last
        mask[:-1] &= ~greater_than_the_last
        return mask
    
    0 讨论(0)
  • 2020-11-22 15:29
    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 < 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. For a huge data set, it will give lots of maximas/minimas so in that case smooth the curve first and then apply this algorithm.

    0 讨论(0)
  • 2020-11-22 15:31

    Another solution using essentially a dilate operator:

    import numpy as np
    from scipy.ndimage import rank_filter
    
    def find_local_maxima(x):
       x_dilate = rank_filter(x, -1, size=3)
       return x_dilate == x
    
    

    and for the minima:

    def find_local_minima(x):
       x_erode = rank_filter(x, -0, size=3)
       return x_erode == x
    
    

    Also, from scipy.ndimage you can replace rank_filter(x, -1, size=3) with grey_dilation and rank_filter(x, 0, size=3) with grey_erosion. This won't require a local sort, so it is slightly faster.

    0 讨论(0)
  • 2020-11-22 15:34

    If you are looking for all entries in the 1d array a smaller than their neighbors, you can try

    numpy.r_[True, a[1:] < a[:-1]] & numpy.r_[a[:-1] < a[1:], True]
    

    You could also smooth your array before this step using numpy.convolve().

    I don't think there is a dedicated function for this.

    0 讨论(0)
  • 2020-11-22 15:37

    None of these solutions worked for me since I wanted to find peaks in the center of repeating values as well. for example, in

    ar = np.array([0,1,2,2,2,1,3,3,3,2,5,0])

    the answer should be

    array([ 3,  7, 10], dtype=int64)
    

    I did this using a loop. I know it's not super clean, but it gets the job done.

    def findLocalMaxima(ar):
    # find local maxima of array, including centers of repeating elements    
    maxInd = np.zeros_like(ar)
    peakVar = -np.inf
    i = -1
    while i < len(ar)-1:
    #for i in range(len(ar)):
        i += 1
        if peakVar < ar[i]:
            peakVar = ar[i]
            for j in range(i,len(ar)):
                if peakVar < ar[j]:
                    break
                elif peakVar == ar[j]:
                    continue
                elif peakVar > ar[j]:
                    peakInd = i + np.floor(abs(i-j)/2)
                    maxInd[peakInd.astype(int)] = 1
                    i = j
                    break
        peakVar = ar[i]
    maxInd = np.where(maxInd)[0]
    return maxInd 
    
    0 讨论(0)
提交回复
热议问题