ProGM
ProGM

Reputation: 7108

How to efficiently turn a boolean numpy array into threshold boolean array?

I'm using Python 2.7 and NumPy to work on big arrays of boolean values.

I have an array A, that is something like this:

>>> A
array([[[False, False, True, True, True],
        [False, False, False, True, True],
        [False, False, True, True, True],
        [False, False, False, True, True],
        [False, False, False, False, True]],

       [[False, True, True, True, True],
        [False, True, True, True, True],
        [False, False, True, True, True],
        [False, True, True, True, True],
        [False, False, True, True, True]]])

I have to turn it in a boolean array like this:

>>> B
array([[[True, False, True, True, True],
        [True, True, False, True, True],
        [True, False, True, True, True],
        [True, True, False, True, True],
        [True, True, True, False, True]],

       [[False, True, True, True, True],
        [False, True, True, True, True],
        [True, False, True, True, True],
        [False, True, True, True, True],
        [True, False, True, True, True]]])

So the idea is that the last False value of each row should remain and any other value should become True. I need to create it in order to use it as a mask for another array.

Is there a way to do it with NumPy without using for loops (that are quite slow)?

Upvotes: 3

Views: 1054

Answers (4)

Alex Riley
Alex Riley

Reputation: 176730

Here is one way that works for your array (and also works for arrays with more mixed up rows such as [F, T, T, F, T] too):

>>> x = 4 - np.argmin(A[:,:,::-1], axis=2)[:,:,np.newaxis]
>>> (np.arange(5) * np.ones_like(A)) != x
array([[[ True, False,  True,  True,  True],
        [ True,  True, False,  True,  True],
        [ True, False,  True,  True,  True],
        [ True,  True, False,  True,  True],
        [ True,  True,  True, False,  True]],

       [[False,  True,  True,  True,  True],
        [False,  True,  True,  True,  True],
        [ True, False,  True,  True,  True],
        [False,  True,  True,  True,  True],
        [ True, False,  True,  True,  True]]], dtype=bool)

Explanation:

  • Flip the array A on axis=2 and use argmin across that axis to get the index of the first False value.

  • We need to know the index of the last occurrence of False in A (not the first occurrence in the flipped array). This is the value of the depth of the array (i.e. 5), minus 1, minus the index found in the previous step.

  • Make this new array of indices compatible with A by adding a new axis (axis=2). Call this new array of indices x.

  • Construct an array of the same dimensions as A where each row of axis=2 is np.arange(5). The desired boolean array is found by testing for inequality of this constructed array with x.

Upvotes: 3

hildensia
hildensia

Reputation: 1740

You could also use the xor operator ^ for that purpose. Simply "leftshift" the array by one and add True values to the right and then xor the new and the old array:

A = np.array([[False, False, True, True, True],
              [False, False, False, True, True],
              [False, False, True, True, True],
              [False, False, False, True, True],
              [False, False, False, False, True]])

X = np.hstack((A[:,1:], 
               np.array(np.ones((A.shape[0], 1)), dtype=np.bool))))
>>> array([[False, True, True, True, True],
           [False, False, True, True, True],
           [False, True, True, True, True],
           [False, False, True, True, True],
           [False, False, False, True, True]])

np.invert(A ^ X)
>>> array([[True, False, True, True, True],
           [True, True, False, True, True],
           [True, False, True, True, True],
           [True, True, False, True, True],
           [True, True, True, False, True]])

This only works if all False values are left and followed by only True values.

Upvotes: 3

Irshad Bhat
Irshad Bhat

Reputation: 8709

This is a simple problem. You need to select each row. Find the positions of False using np.where() and place True in those positions except the last one denoted by index [-1]. This is done in the code given below:

>>> import numpy as np
>>> A=np.array([[[False, False, True, True, True],
         [False, False, False, True, True],
         [False, False, True, True, True],
         [False, False, False, True, True],
         [False, False, False, False, True]],

        [[False, True, True, True, True],
         [False, True, True, True, True],
         [False, False, True, True, True],
         [False, True, True, True, True],
         [False, False, True, True, True]]])

>>> for mat in A:
     opmat=[]
     for arr in mat:
         index=np.where(arr==False)
         arr[index[0][:-1]]=True
         opmat.append(arr)
     out.append(opmat)

>>> out=np.array(out)
>>> out
array([[[ True, False,  True,  True,  True],
        [ True,  True, False,  True,  True],
        [ True, False,  True,  True,  True],
        [ True,  True, False,  True,  True],
        [ True,  True,  True, False,  True]],

       [[False,  True,  True,  True,  True],
        [False,  True,  True,  True,  True],
        [ True, False,  True,  True,  True],
        [False,  True,  True,  True,  True],
        [ True, False,  True,  True,  True]]], dtype=bool)

Upvotes: 0

Marcin
Marcin

Reputation: 49816

If you invert the array, you can use numpy.nonzero to find all the (originally) false entries, then take the last one in each row from that. You can then use that to construct your mask array.

Upvotes: 1

Related Questions