Home > OS >  Find local maxima index of element not surrounded by zeros
Find local maxima index of element not surrounded by zeros

Time:05-17

I am trying to identify the indexes of local maxima not surrounded by zeros of a 1D numpy array.

The original code is:

max_idx = [
    i for i in range(1, len(elem_array) - 1)
    if ((elem_array[i - 1] < elem_array[i]) and (elem_array[i   1] <= elem_array[i]))
    and ((elem_array[i - 1] != 0) or (elem_array[i   1] != 0))
]

With this code using the array:

elem_array = np.array([23,  0, 45,  0, 12, 13, 14,  0,  0,  0,  1, 67,  1])

the result is: max_idx = [6, 11].

Important: the element i can be greater or equal to element i 1, but just greater than element i-1 and the 0 can be only in 1 side of the element i, this is the reason why 45 is not recognised as a local maximum.

I was trying to modify it with scipy.signal.argrelextrema, but this gives me the result: max_idx = [2, 6, 11], which contains an extra element.

And with the array:

elem_array = np.array([0.0, 0.0, 0.0, 0.0, 0.07, 0.2, 0.4, 0.6, 0.8, 0.9, 1.0, 1.0, 1.0, 1.0, 1.0])

the result is an empty array, when it should be: max_idx = [10].

Do you have any suggestion how the original code could be modified? Thanks

CodePudding user response:

You can use numpy.lib.stride_tricks.sliding_window_view to create a sliding window of shape 3 and then apply conditions in vectorized way:

import numpy as np

def get_local_maxima(a: np.array, window_shape: int = 3) -> np.array:
    
    mid_index = window_shape//2
    # Adding initial and final zeros and create the windo of given size
    window = np.lib.stride_tricks.sliding_window_view(np.array([0]*mid_index   [*a]   [0]*mid_index), window_shape)
    
    c1 = np.argmax(window, axis=1)==mid_index # first condition is that the max must be in the center of the window
    c2 = (window[:, [i for i in range(window_shape) if i!=mid_index]]!=0).any(axis=1) # second condition is that one among 0-th and 2-nd element must be non-zero
    
    return np.argwhere(c1 & c2)

a = np.array([23,  0, 45,  0, 12, 13, 14,  0,  0,  0,  1, 67,  1])
b = np.array([0.0, 0.0, 0.0, 0.0, 0.07, 0.2, 0.4, 0.6, 0.8, 0.9, 1.0, 1.0, 1.0, 1.0, 1.0])

get_local_maxima(a)
array([[ 6],
       [11]])

get_local_maxima(b)
array([[10]])

CodePudding user response:

A loop like this is pretty straightforward to vectorize:

mask = (
    (elem_array[:-2] < elem_array[1:-1])
    & (elem_array[2:] <= elem_array[1:-1])
    & ((elem_array[:-2] != 0) | (elem_array[2:] != 0))
)
max_idx = np.nonzero(mask)[0]   1
  • Related