How to change the array elements according specific condition

Question:

I have an array for an example:

import numpy as np

data=np.array([[4,4,4,0,1,1,1,0,0,0,0,1,0,0,1],
           [3,0,0,1,1,1,1,1,1,1,1,0,0,1,0],
           [6,0,0,1,1,1,1,1,0,0,0,0,1,0,0]])

Requirement :
In the data array, if element 1’s are consecutive as the square size
of ((3,3)) and more than square size no changes. Otherwise, replace
element value 1 with zero except the square size.

Expected output :

               [[4 4 4 0 1 1 1 0 0 0 0 0 0 0 0]
                [3 0 0 0 1 1 1 0 0 0 0 0 0 0 0]
                [6 0 0 0 1 1 1 0 0 0 0 0 0 0 0]]
Asked By: Bhar Jay

||

Answers:

Should be easy to do with a double for loop and a second array

rows = len(source_array)
columns = len(source_array[0])

# Create a result array of same size
result_array = [[0 for _ in range(rows)] for _ in range(columns)]

for i in range(rows):
    for j in range(columns):

         # Copy non 1s
         if source_array[i][j] != 1:
             result_array[i][j] = source_array[i][j]

     # if enough rows left to check then check
     if i < rows - 3:
         if j < columns - 3:
             
             # Create set on the selected partition
             elements = set(source_array[i][j:j+3] + source_array[i+1][j:j+3] + source_array[i+2][j:j+3])

             # Copy 1s to new array
             if len(elements) == 1 and 1 in elements:
                  for sq_i in range(i,i+3):
                      for sq_j in range(j,j+3):
                          result_array[sq_i][sq_j] = 1
Answered By: Karan Venkatesh

I will provide here as solutions two different approaches. One which doesn’t and one which is using Python loops. Let’s start with the common header:

import numpy as np
from skimage.util import view_as_windows as winview
data=np.array([[4,4,4,0,1,1,1,0,0,0,0,1,0,0,1],
               [3,0,0,1,1,1,1,1,1,1,1,0,0,1,0],
               [6,0,0,1,1,1,1,1,0,0,0,0,1,0,0]])

Below an approach without using Python loops resulting in shortest code, but requiring import of an additional module skimage:

clmn = np.where(np.all(winview(data,(3,3))[0],axis=(1,2)))[0][0]
data[data == 1]       = 0 # set all ONEs  to zero 
data[0:3,clmn+3:]     = 0 # set after match to zero
data[0:3,clmn:clmn+3] = 1 # restore ONEs

Another one is using Python loops and only two lines longer:

for clmn in range(0,data.shape[1]):
    if np.all(data[0:3,clmn:clmn+3]):
        data[data==1] = 0
        data[0:3,clmn+3:] = 0
        data[0:3,clmn:clmn+3] = 1
        break

Instead of explaining how the above code using loops works I have put the ‘explanations’ into the names of the used variables so the code becomes hopefully self-explaining. With this explanations and some redundant code you can use the code below for another shaped haystack to search for in another array of same kind. For an array with more rows as the shape of the sub-array there will be necessary to loop also over the rows and optimize the code skipping some unnecessary checks.

import numpy as np
data=np.array([[4,4,4,0,1,1,1,0,0,0,0,1,0,0,1],
               [3,0,0,1,1,1,1,1,1,1,1,0,0,1,0],
               [6,0,0,1,1,1,1,1,0,0,0,0,1,0,0]])
indx_of_clmns_in_shape = 1
indx_of_rows_in_shape  = 0
subarr_shape = (3, 3)
first_row  = 0
first_clmn = 0
for clmn in range(first_clmn,data.shape[indx_of_clmns_in_shape],1):
    sub_data = data[
        first_row:first_row+subarr_shape[indx_of_rows_in_shape],
        clmn:clmn+subarr_shape[indx_of_clmns_in_shape]]
    if np.all(sub_data):
        data[data == 1] = 0
        data[first_row : subarr_shape[indx_of_rows_in_shape], 
             clmn+subarr_shape[indx_of_clmns_in_shape] : ] = 0
        data[first_row : subarr_shape[indx_of_rows_in_shape], 
             clmn : clmn+subarr_shape[indx_of_clmns_in_shape]] = 1
        break
    # print(sub_data)
print(data)

all three versions of the code give the same result:

[[4 4 4 0 1 1 1 0 0 0 0 0 0 0 0]
 [3 0 0 0 1 1 1 0 0 0 0 0 0 0 0]
 [6 0 0 0 1 1 1 0 0 0 0 0 0 0 0]]
Answered By: Claudio
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.