Disable negative indexing when checking neighbours in a matrix in Python

Viewed 100

So I have the following code with given input:

import numpy as np

x = np.matrix([[1,1,1,1,0],
               [1,1,1,0,0],
               [1,1,0,0,0],
               [1,0,0,0,0]])
print(x)

def MIZ(mat,check):
    for j in range(0,mat.shape[0]):
        for i in range(0,mat.shape[1]):
            try:
                if mat[i,j] == 1:
                    if mat[i+check,j] == 0 or \
                       mat[i-check,j] == 0 or \
                       mat[i,j+check] == 0 or \
                       mat[i,j-check] == 0:
                           mat[i,j] = 2
            except:
                pass
    return mat
            
print(MIZ(x,1))

The idea is is quite simple; in that all 1s that lie next to 0s are converted to 2s. The "check" parameter here should ideally be adjustable such that if "check=2" 1s that have a 0 a space away are also converted to a 2, and so on. Now the problem arises with the first element in the matrix (index = 0,0), because then i-1 and j-1 are both -1 and for my purposes I want to avoid this and limit it ONLY to surrounding elements.

The current result of the code is:

[[2 2 2 2 0]
 [2 1 2 0 0]
 [2 2 0 0 0]
 [1 0 0 0 0]]

There's also the issue of the lowermost 1 not being converted to 2 but that's a secondary issue.

2 Answers

You can check before indexing that you are in range. Like that (btw look that i changed i and j in the loops):

def in_range(index, range_length):
    return -range_length <= index <= range_length -1


def MIZ(mat, check):
    for i in range(0, mat.shape[0]):
        for j in range(0, mat.shape[1]):
            if mat[i, j] == 1:
                if (in_range(i+check, mat.shape[0]) and mat[i + check, j] == 0) or \
                   (in_range(i-check, mat.shape[0]) and mat[i - check, j] == 0) or \
                    (in_range(j + check, mat.shape[1]) and mat[i, j + check] == 0) or \
                    (in_range(j - check, mat.shape[1]) and mat[i, j - check] == 0):
                    mat[i, j] = 2
    return mat

print(MIZ(x, 1))

Input:

x = np.array([[1,1,1,1,0],
               [1,1,1,0,0],
               [1,1,0,0,0],
               [1,0,0,0,0]])

Output:

[[2 2 2 2 0]
 [2 1 2 0 0]
 [2 2 0 0 0]
 [2 0 0 0 0]]

edit, changing the "in_range" function should make it that you only access non-negative indices as you want:

def in_range(index, range_length):
    return 0 <= index <= range_length -1

Input:

x = np.array([[1,1,1,1,0],
               [1,1,1,0,0],
               [1,1,0,0,0],
               [1,0,0,0,0]])

Output:

[[1 1 1 2 0]
 [1 1 2 0 0]
 [1 2 0 0 0]
 [2 0 0 0 0]]

I have not been able to find any clean solution but this does the job. Max(0, value) is also possible but then if you have a negative value it picks 0, which is an index itself.

# Function
def no_neg(value):
    if value >= 0:
        return value
    else:
        raise IndexError
        

In your code it looks something like this. import numpy as np

x = np.matrix([[1,1,1,1,0],
               [1,1,1,0,0],
               [1,1,0,0,0],
               [1,0,0,0,0]])
print(x)

def MIZ(mat,check):
    for j in range(0,mat.shape[0]):
        for i in range(0,mat.shape[1]):
            try:
                if mat[i,j] == 1:
                    if mat[no_neg(i+check),no_neg(j)] == 0 or \
                       mat[no_neg(i-check),no_neg(j)] == 0 or \
                       mat[no_neg(i),no_neg(j+check)] == 0 or \
                       mat[no_neg(i),no_neg(j-check)] == 0:
                           mat[i,j] = 2
            except:
                pass
    return mat
            
print(MIZ(x,1))
Related