2

This is my original 2d array A

[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
 [0, 2, 2, 2, 2, 2, 2, 0, 0, 0],
 [0, 2, 2, 2, 2, 2, 2, 0, 0, 0],
 [0, 2, 2, 2, 2, 2, 2, 0, 0, 0],
 [0, 2, 2, 2, 2, 2, 2, 0, 0, 0],
 [0, 2, 2, 8, 8, 8, 2, 0, 0, 0],
 [0, 2, 2, 8, 8, 8, 2, 0, 0, 0],
 [0, 0, 0, 8, 8, 8, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]

Let's say I want to return a 3x3 submatrix of 8's at the middle. I made a boolean mask with this expression A == 8 and it looks like this.

array([[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, 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,  True,  True, False, False, False,
        False],
       [False, False, False,  True,  True,  True, False, False, False,
        False],
       [False, False, False,  True,  True,  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]])

This is the point where I'm stuck. How can I return the submatrix with that boolean mask? If I do A[A == 8], it returns a flat array of 8s like this

array([8, 8, 8, 8, 8, 8, 8, 8, 8])

Another way is getting the row and column numbers with np.where(A == 8) which returns (array([5, 5, 5, 6, 6, 6, 7, 7, 7]), array([3, 4, 5, 3, 4, 5, 3, 4, 5])). How can I return the matrix using them?

Is there any better approach for this problem?

3
  • Boolean indexing like this returns a 1d result. In general such indexing does not a return a neat block of values. But if you know the results will be such block, just reshape them. Commented Feb 18, 2020 at 16:38
  • @hpaulj I don't know the shape Commented Feb 18, 2020 at 16:41
  • 3
    It works as intended, because you don't always have rectangular shape, eg. A[A==2]. Now if you are certain that 8 only appear in a block, you can detect the shape with argmin/argmax. Commented Feb 18, 2020 at 17:16

3 Answers 3

1

As mentioned in comments, A[A==some_value] correctly returns a rank-1 array of values some_value. That makes sense because it's not always the case that the values some_value are organized in a block (take for example some_value = 2) or the number of values some_value is such that they cannot be put into a 2D array.

However If you are sure that there is such a block, you can do the following to get it:

import numpy as np

inds = np.where(A==8)
slice_x = slice(inds[0].min(), inds[0].max() + 1)  # slice(6, 9, None)
slice_y = slice(inds[1].min(), inds[1].max() + 1)  # slice(3, 6, None)

A[slice_x, slice_y]

# array([[8, 8, 8],
#        [8, 8, 8],
#        [8, 8, 8]])

As alternative, you can use scipy.ndimage.find_objects to obtain the slices:

from scipy import ndimage

slice_x, slice_y = ndimage.find_objects(A==8)[0]  # (slice(6, 9, None), slice(3, 6, None))
Sign up to request clarification or add additional context in comments.

Comments

1

You can capture row and column indexes and after that return submatrix from these indexes only using np.ix_ method:

x, y = np.where(A==8) #[5 5 5 6 6 6 7 7 7], [3 4 5 3 4 5 3 4 5]
x, y = np.unique(x), np.unique(y) # [5,6,7], [3,4,5]
print(A[np.ix_(x, y)]) #prints [[8 8 8], [8 8 8], [8 8 8]]

This should work even in more general cases, although not always as expected:

def submatrix(A):
    x, y = np.where(A==8)
    x, y = np.unique(x), np.unique(y)
    return A[np.ix_(x, y)]

>>> A = np.array([
[8, 2, 8, 2, 8, 0],
[2, 8, 8, 8, 2, 0],
[2, 8, 4, 8, 3, 0],
[0, 8, 8, 8, 0, 0],
[8, 0, 8, 0, 8, 0],
[0, 0, 0, 0, 0, 0]])
>>> submatrix(A)
array([[8, 2, 8, 2, 8],
       [2, 8, 8, 8, 2],
       [2, 8, 4, 8, 3],
       [0, 8, 8, 8, 0],
       [8, 0, 8, 0, 8]])
>>> A = np.array([
[8, 2, 8, 2, 8, 0],
[2, 2, 8, 8, 2, 0],
[2, 2, 4, 3, 3, 0],
[0, 2, 8, 8, 0, 0],
[8, 0, 8, 0, 8, 0],
[0, 0, 0, 0, 0, 0]])
>>> submatrix(A) # skipping empty rows and columns
array([[8, 8, 2, 8],
       [2, 8, 8, 2],
       [0, 8, 8, 0],
       [8, 8, 0, 8]])

Since np.unique returns sorted arrays, you can fill the gaps constucting x, y like so:

def submatrix(A):
    x, y = np.where(A==8)
    x, y = np.unique(x), np.unique(y)
    x, y = np.arange(x[0], x[-1]+1), np.arange(y[0], y[-1]+1)
    return A[np.ix_(x, y)]

Comments

0

You can try the following

 data = A[np.any(A==8,axis=1)]
 data.T[np.all(data==8,axis=0)]

This should give,

 array([[8, 8, 8],
        [8, 8, 8],
        [8, 8, 8]])

Comments

Your Answer

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.