How to get all the values of neighbours around an element in matrix?

Question:

I need to get the values of all the neighbors around an element in a matrix in python. Suppose I have a matrix like below,

matrix=[
    [1, 2, 3, 4],
    [5, 6, 7, 8],
    [9,10,11,12]
]

For the first element i.e., matrix[0][0], the neighbors are [2,5,6].

For matrix[0][1], the neighbors are [1,3,5,6,7].

For matrix[0][2], the neighbors are [2,4,6,7,8].

For a given element, i need to get these list of values.

I can do the same by comparing i,j values when i=0,j=0 get matrix[0][1], matrix[1][0], matrix[1][1] using switch case and so. But it will become lengthy code. Is there any inbuilt function or any module to make the above task even simpler?

Asked By: Ssukumar

||

Answers:

You can build a dictionary of coordinate and corresponding value in the matrix for simpler lookup:

matrix=[[1,2,3,4],
        [5,6,7,8],
        [9,10,11,12]]

def get_neighbors(a, b):
  d = {(i, c):matrix[i][c] for i in range(len(matrix)) for c in range(len(matrix[0]))}
  return filter(None, [d.get(i) for i in
    [(a+1, b+1), (a, b+1), (a+1, b), (a-1, b+1), (a-1, b), (a, b-1), (a+1, b-1)]])

cords = [(0, 0), (0, 1), (0, 2)]
results = [get_neighbors(*i) for i in cords]

Output:

[[6, 2, 5], [7, 3, 6, 1, 5], [8, 4, 7, 2, 6]]
Answered By: Ajax1234

If you don’t care about efficiency, use scipy:

import scipy, scipy.ndimage

def nb_vals(matrix, indices):
    matrix = scipy.array(matrix)
    indices = tuple(scipy.transpose(scipy.atleast_2d(indices)))
    arr_shape = scipy.shape(matrix)
    dist = scipy.ones(arr_shape)
    dist[indices] = 0
    dist = scipy.ndimage.distance_transform_cdt(dist, metric='chessboard')
    nb_indices = scipy.transpose(scipy.nonzero(dist == 1))
    return [matrix[tuple(ind)] for ind in nb_indices]

e.g.

>>> matrix=[[1,2,3,4],
... [5,6,7,8],
... [9,10,11,12]]
>>>
>>> nb_vals(matrix, [1,1])
[1,2,3,5,7,9,10,11]

This is dimension agnostic (works for an arbitrary number of dimensions for the input “matrix”) and handles an arbitrary number of indices which you may wish to find neighbours around.

>>> arr_shape = (2,3,4,5)
>>> testMatrix = scipy.array(scipy.random.random(arr_shape)*10, dtype=int)
>>> print(testMatrix)
[[[[7 0 0 1 9]
   [9 5 8 5 8]
   [4 0 0 8 0]
   [1 9 1 3 2]]

  [[9 2 3 3 5]
   [2 3 3 7 9]
   [6 5 6 6 2]
   [9 1 1 0 0]]

  [[8 8 5 7 9]
   [9 0 7 7 6]
   [3 8 7 6 4]
   [8 7 5 5 9]]]

 [[[8 9 2 0 0]
   [8 3 5 5 2]
   [4 0 1 0 3]
   [1 0 9 1 3]]

  [[6 9 2 5 2]
   [2 7 5 5 3]
   [6 7 2 9 5]
   [4 2 7 3 1]]

  [[1 7 7 7 6]
   [5 1 4 1 0]
   [3 9 4 9 7]
   [7 7 6 6 7]]]]

>>> nb_vals(testMatrix, [1,2,2,3])
[3, 7, 9, 6, 6, 2, 1, 0, 0, 7, 7, 6, 7, 6, 4, 5, 5, 9, 5, 5, 3, 2, 9, 5, 7, 3, 1, 4, 1, 0, 4, 7, 6, 6, 7]

This solution uses a chessboard-type chamfer transform on an image-like binary array mask, where 1 is equal to a white pixel on the mask and 0 is equal to a black pixel (background) to the mask. The chamfer transform calculates the chessboard distance of all white pixels to the background; all pixel locations with a distance calculated as 1 are the neighbours, and the values at these locations on the input array are returned.

Answered By: dROOOze