joachimnogra
joachimnogra

Reputation: 13

In a matrix for a given index, how do I check if any neighboring values are 2 smaller than it?

I was doing a python challenge and this one stumped me. This is the input matrix (numpy format):

# [[1, 7, 2, 2, 1],
#  [7, 7, 9, 3, 2],
#  [2, 9, 4, 4, 2],
#  [2, 3, 4, 3, 2],
#  [1, 2, 2, 7, 1]]

and the function would output this matrix

# [[False, True, False, False, False],
#  [True, False, True, False, False],
#  [False, True, False, True, False],
#  [False, False, False, False, False],
#  [False, False, False, True, False]]

And you can see the value will be 'true' if any (up/down/left/right) neighbor is 2 smaller than itself. We've been learning numpy, but this doesn't feel like it's too much of a numpy thing).

I tried to do simple if comparison=true checks, but I kept stumbling into out-of-index errors and I couldnt find any way to circumvent/ignore those.

Thanks in advance.


This is the essence of what I've tried so far. I've simplified the task here to simply check the first row horizontally. If I could get this to work, I would extend it to check the next row horizontally until the end, and then I would do the same thing but vertically.

import numpy as np

ex=np.array([[7, 2, 3, 4, 3, 4, 7]])

def count_peaks(A):
    
    matrixHeight=A.shape[0]
    matrixWidth=A.shape[1]
    
    peakTable=np.zeros(shape=(matrixHeight,matrixWidth))
    
    
    for i in range(matrixWidth):
        if A[i]-A[i+1]>=2 or A[i]-A[i-1]>=2:
            peakTable[0,i]=1

    return peakTable

... which of course outputs:

IndexError: index 1 is out of bounds for axis 0 with size 1

as I'm trying to find the value of A[-1] which doesn't exist.

Upvotes: 1

Views: 416

Answers (2)

Richard K Yu
Richard K Yu

Reputation: 2202

If you don't mind me not using numpy to get the solution, but converting to numpy at the end, here is my attempt:

import numpy as np

def check_neighbors(mdarray,i,j):
    neighbors = (-1, 0), (1, 0), (0, -1), (0, 1)

    for neighbor in neighbors:
        try:
            if mdarray[i][j]-mdarray[i+neighbor[0]][j+neighbor[1]]>=2:
                return True
        except IndexError:
            pass

    return False


mdarray= [[1, 7, 2, 2, 1],
        [7, 7, 9, 3, 2],
        [2, 9, 4, 4, 2],
        [2, 3, 4, 3, 2],
        [1, 2, 2, 7, 1]]

peak_matrix =[]

for i in range(len(mdarray)):
    row = []
    for j in range(len(mdarray[i])):
        #print(check_neighbors(mdarray,i,j))
        row.append(check_neighbors(mdarray,i,j))
    peak_matrix.append(row)


y=np.array([np.array(xi) for xi in peak_matrix])
print(y)

I use the try-except block to avoid errors when the index goes out of bounds.

Note: Row 4 Column 3 (starting counts at 1) of my output seems to differ from yours. I think that the 4 and 2 difference in the neighbors should make this entry true?

Output:

[[False  True False False False]
 [ True False  True False False]
 [False  True False  True False]
 [False False  True False False]
 [False False False  True False]]

Edit: changed from bare except to IndexError as Neither suggests in the comments. pass and continue doesn't make a difference in this case but yes.

Upvotes: 0

mozway
mozway

Reputation: 260530

You are using numpy arrays, so don't loop, use vectorial code:

import numpy as np

# get shape
x,y = a.shape

# generate row/col of infinites
col = np.full([x, 1], np.inf)
row = np.full([1, y], np.inf)

# shift left/right/up/down
# and compute difference from initial array
left = a - np.c_[col, a[:,:-1]]
right = a - np.c_[a[:,1:], col]
up = a - np.r_[row, a[:-1,:]]
down = a -np.r_[a[1:,:], row]

# get max of each shift and compare to threshold
peak_table = np.maximum.reduce([left,right,up,down])>=2

# NB. if we wanted to use a maximum threshold, we would use
# `np.minimum` instead and initialize the shifts with `-np.inf`

output:

array([[False,  True, False, False, False],
       [ True, False,  True, False, False],
       [False,  True, False,  True, False],
       [False, False,  True, False, False],
       [False, False, False,  True, False]])

input:

import numpy as np
a = np.array([[1, 7, 2, 2, 1],
              [7, 7, 9, 3, 2],
              [2, 9, 4, 4, 2],
              [2, 3, 4, 3, 2],
              [1, 2, 2, 7, 1]])

Upvotes: 2

Related Questions