Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I extract a 2D NumPy sub-array from a 2D NumPy array based on patterns?

I have a 2D NumPy array which looks like this:


Array=
[
[0,0,0,0,0,0,0,2,2,2],
[0,0,0,0,0,0,0,2,2,2].
[0,0,1,1,1,0,0,2,2,2],
[0,0,1,1,1,0,0,2,2,2],
[0,0,1,1,1,0,0,1,1,1],
[0,0,0,0,0,0,0,1,1,1]
]

I need to display the arrays of non-zero elements as:

Array1:
[
[1,1,1],
[1,1,1],
[1,1,1]
]

Array2:
[
[2,2,2],
[2,2,2],
[2,2,2],
[2,2,2]
]

Array3:
[
[1,1,1],
[1,1,1]
]

Could someone please help me out with what logic I could use to achieve the following output? I can't use fixed indexes (like array[a:b, c:d]) since the logic i create should be able to work for any NumPy array with a similar pattern.

like image 347
MajesticOverlord Avatar asked Feb 18 '26 07:02

MajesticOverlord


2 Answers

This uses scipy.ndimage.label to recursively identify disconnected sub-arrays.

import numpy as np
from scipy.ndimage import label

array = np.array(
    [[0,0,0,0,0,0,0,2,2,2,3,3,3],
     [0,0,0,0,0,0,0,2,2,2,0,0,1],
     [0,0,1,1,1,0,0,2,2,2,0,2,1],
     [0,0,1,1,1,0,0,2,2,2,0,2,0],
     [0,0,1,1,1,0,0,1,1,1,0,0,0],
     [0,0,0,0,0,0,0,1,1,1,0,0,0]])
# initialize list to collect sub-arrays
arr_list = []

def append_subarrays(arr, val, val_0):
    '''
    arr : 2D array
    val : the value used for filtering
    val_0 : the original value, which we want to preserve
    '''

    # remove everything that's not the current val
    arr[arr != val] = 0
    if 0 in arr:  # <-- not a single rectangle yet
        # get relevant indices as well as their minima and maxima
        x_ind, y_ind = np.where(arr != 0)
        min_x, max_x, min_y, max_y = min(x_ind), max(x_ind) + 1, min(y_ind), max(y_ind) + 1
        # cut subarray (everything corresponding to val)
        arr = arr[min_x:max_x, min_y:max_y]
        # use the label function to assign different values to disconnected regions
        labeled_arr = label(arr)[0]
        # recursively apply append_subarrays to each disconnected region 
        for sub_val in np.unique(labeled_arr[labeled_arr != 0]):
            append_subarrays(labeled_arr.copy(), sub_val, val_0)

    else:  # <-- we only have a single rectangle left ==> append
        arr_list.append(arr * val_0)

for i in np.unique(array[array > 0]):
    append_subarrays(array.copy(), i, i)

for arr in arr_list:
    print(arr, end='\n'*2)

Output (note: modified example array):

[[1]
 [1]]

[[1 1 1]
 [1 1 1]
 [1 1 1]]

[[1 1 1]
 [1 1 1]]

[[2 2 2]
 [2 2 2]
 [2 2 2]
 [2 2 2]]

[[2]
 [2]]

[[3 3 3]]

like image 130
mcsoini Avatar answered Feb 19 '26 19:02

mcsoini


This sounds like a floodfill problem, so skimage.measure.label is a good approach:

Array=np.array([[0,0,0,0,0,0,0,2,2,2],
                [0,0,0,0,0,0,0,2,2,2],
                [0,0,1,1,1,0,0,2,2,2],
                [0,0,1,1,1,0,0,2,2,2],
                [0,0,1,1,1,0,0,1,1,1],
                [0,0,0,0,0,0,0,1,1,1]
                ])

from skimage.measure import label
labels = label(Array, connectivity=1)

for label in range(1, labels.max()+1):
    xs, ys = np.where(labels==label)
    shape = (len(np.unique(xs)), len(np.unique(ys)))

    print(Array[xs, ys].reshape(shape))

Output:

[[2 2 2]
 [2 2 2]
 [2 2 2]
 [2 2 2]]
[[1 1 1]
 [1 1 1]
 [1 1 1]]
[[1 1 1]
 [1 1 1]]
like image 36
Quang Hoang Avatar answered Feb 19 '26 19:02

Quang Hoang