9
\$\begingroup\$

I have a a python function for taking in a 2D numpy array and checking if each element is the same as its neighbor elements. I feel like there's a more efficient way to do this but I'm not sure. Here is the code:

import numpy as np
def compare_neighbors(arr):
 '''
 Checks if element (i,j) is different than (i-1,j),(i+1,j),(i,j-1), or 
 (i,j+1).
 --Input--
 arr: (2D np.array) array to compare all elements of
 --Returns--
 comp_arr: (2D bool np.array) bool array with the resulting comparisons.
 True means the original element is the same as its neighbors,
 False means it was different than at least neighbor
 '''
 comp_arr = np.full(arr.shape, False, dtype=bool) #initialize
 arr_height = arr.shape[0]
 arr_width = arr.shape[1]
 for i in range(arr_height): #Row
 for j in range(arr_width): #column
 center = arr[i,j]
 #Check edges
 if i == 0: #left side
 left = arr[i,j]
 else:
 left = arr[i-1, j]
 if i == arr_height - 1: #right side
 right = arr[i,j]
 else:
 right = arr[i+1,j]
 if j == 0: #up
 up = arr[i,j]
 else:
 up = arr[i, j-1]
 if j == arr_width - 1: #down
 down = arr[i,j]
 else:
 down = arr[i, j+1]
 comp_arr[i,j] = len(set([left, right, up, down, center])) == 1
 return comp_arr

If it is helpful, here are the tests I used for testing it:

A = np.array([[1,1],
 [1,1]])
comp_arr_A = compare_neighbors(A)
B = np.array([[2,2],
 [2,2]])
comp_arr_B = compare_neighbors(B)
C = np.array([[1,1,1,1,1,1,1,1,1],
 [1,1,1,1,1,1,1,1,1],
 [1,2,2,2,2,2,2,2,1],
 [1,2,2,1,1,1,2,2,1],
 [1,2,2,2,2,2,2,2,1],
 [1,1,1,1,1,1,1,1,1]])
comp_arr_C = compare_neighbors(C)
D = np.array([[1,1,1],
 [1,2,1],
 [1,1,1]])
comp_arr_D = compare_neighbors(D)
print(A)
print()
print(comp_arr_A)
print()
print(B)
print()
print(comp_arr_B)
print()
print(C)
print()
print(comp_arr_C)
print()
print(D)
print()
print(comp_arr_D)

which returns

[[1 1]
 [1 1]]
[[ True True]
 [ True True]]
[[2 2]
 [2 2]]
[[ True True]
 [ True True]]
[[1 1 1 1 1 1 1 1 1]
 [1 1 1 1 1 1 1 1 1]
 [1 2 2 2 2 2 2 2 1]
 [1 2 2 1 1 1 2 2 1]
 [1 2 2 2 2 2 2 2 1]
 [1 1 1 1 1 1 1 1 1]]
[[ True True True True True True True True True]
 [ True False False False False False False False True]
 [False False False False False False False False False]
 [False False False False False False False False False]
 [False False False False False False False False False]
 [ True False False False False False False False True]]
[[1 1 1]
 [1 2 1]
 [1 1 1]]
[[ True False True]
 [False False False]
 [ True False True]]

as expected. All I need the function to do is check each element and compare it to its left, right, above, and bellow neighbors. If it is the same as them then the compare_array in that index is True and if it is different than any of its neighbors then False.

asked Oct 23, 2017 at 13:14
\$\endgroup\$
1

2 Answers 2

5
\$\begingroup\$

You can improve iterating over the array by using np.ndenumerate to get the current coordinates and current item. From the coordinates you can derive the neighbouring elements.
You can also use inverse checking to only set the respective field to False if a neighbor does not match:

from contextlib import suppress
def compare_neighbors(arr):
 comp_arr = np.full(arr.shape, True, dtype=bool)
 for (x, y), item in np.ndenumerate(arr):
 # Check left.
 if x >= 0:
 if arr[x-1, y] != item:
 comp_arr[x, y] = False
 continue
 # Check right.
 with suppress(IndexError):
 if arr[x+1, y] != item:
 comp_arr[x, y] = False
 continue
 # Check top.
 with suppress(IndexError):
 if arr[x, y+1] != item:
 comp_arr[x, y] = False
 continue
 # Check bottom.
 if y >= 0:
 if arr[x, y-1] != item:
 comp_arr[x, y] = False
 continue
 return comp_arr
answered Oct 23, 2017 at 14:05
\$\endgroup\$
3
\$\begingroup\$

You can also use the np.roll function if you have a bigger array like in a post by doing:

import numpy as np
def shift_helper(array, shift=0, axis=0):
 # Roll the array by n unity along one axis
 _array = np.roll(_array, shift=shift, axis=axis)
 # Cancel the last/first slice rolled to the first/last slice
 if axis == 0:
 if shift >= 0:
 _array[:1, :, :] = False
 else:
 _array[-1:, :, :] = False
 return _array
 elif axis == 1:
 if shift >= 0:
 _array[:, :1, :] = False
 else:
 _array[:, -1:, :] = False
 return _array
 #Uncomment it for 3D array
 #elif axis == 2:
 #if shift >= 0:
 #_array[:, :, :1] = False
 #else:
 #_array[:, :, -1:] = False
 #return _array
def compare(array, that_value):
 bool_array = np.zeros(array.shape, dtype=bool)
 bool_array[np.where((array == that_value)
 & (shift_helper(array!=that_value, shift=1, axis=0)#up
 | shift_helper(array!=that_value, shift=-1, axis=0)#down
 | shift_helper(array!=that_value, shift=1, axis=1)#left
 | shift_helper(array!=that_value, shift=-1, axis=1)#right
 #Uncomment below for 3D array
 #| shift_helper(array!=that_value, shift=1, axis=2)#front
 #| shift_helper(array!=that_value, shift=-1, axis=2)#back
 ))] = True
 return bool_array
# Main
C = np.array([[1,1,1,1,1,1,1,1,1],
 [1,1,1,1,1,1,1,1,1],
 [1,2,2,2,2,2,2,2,1],
 [1,2,2,1,1,1,2,2,1],
 [1,2,2,2,2,2,2,2,1],
 [1,1,1,1,1,1,1,1,1]])
print(compare(C, 1))
print(compare(C, 2))

You can choose the value that you want to compare and extend to 3D with this.

answered Jul 3, 2018 at 10:00
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.