Prashanth
Prashanth

Reputation: 115

Efficient way to do conditional checks on Numpy Arrays

I am trying to perform some conditional checks on numpy arrays and my code right now is not very pythonic. Can someone suggest more efficient ways to write the code below ?

  1. h is a 2D numpy array of floats with dimension nrows*ncols
  2. ibound is a 2D numpy array of ints with dimension nrows*ncols
  3. L1TopOld is a 2D numpy array of floats with dimension nrows*ncols
eps = 1.0e-04
#Fill in gaps in the surfaces#####################################
for i in range(nrows):
  for j in range(ncols):
      if (ibound[i,j] == 1 and fabs(h[i,j]-nodata) <= eps):
          h[i,j] = L1TopOld[i,j]

Upvotes: 1

Views: 458

Answers (3)

Taha
Taha

Reputation: 778

You only need to proceed the vectorial way:

cond = (ibound == 1) & (np.fabs(h - nodata) <= eps)
h[cond] = L1TopOld[cond]

Let's see an example (I am using the one proposed in the previous comment):

>>> import numpy as np
>>> h = np.random.random((3, 4))
>>> nodata = 10
>>> h.flat[[2, 3, 4, 7, 8, 9]] = nodata
>>> ibound = np.random.randint(0, 2, (3, 4))
>>> L1TopOld = np.ones((3, 4)) * 5
>>> eps = 0.01

>>> ibound
array([[0, 0, 1, 0],
       [0, 0, 1, 1],
       [1, 1, 1, 1]])
>>> L1TopOld
array([[ 5.,  5.,  5.,  5.],
       [ 5.,  5.,  5.,  5.],
       [ 5.,  5.,  5.,  5.]])
>>> h
array([[  0.89332453,   0.71094897,  10.        ,  10.        ],
       [ 10.        ,   0.47419211,   0.50206745,  10.        ],
       [ 10.        ,  10.        ,   0.71388832,   0.84379527]])

>>> cond = (ibound == 1) & (np.fabs(h - nodata) <= eps)
>>> cond
array([[False, False,  True, False],
       [False, False, False,  True],
       [ True,  True, False, False]], dtype=bool)

>>> h[cond] = L1TopOld[cond]
>>> h
array([[  0.89332453,   0.71094897,   5.        ,  10.        ],
       [ 10.        ,   0.47419211,   0.50206745,   5.        ],
       [  5.        ,   5.        ,   0.71388832,   0.84379527]])

Yours

Upvotes: 1

wwii
wwii

Reputation: 23743

If you want to modify the original array:

import numpy as np
eps = .01
nodata = 10
h = np.array([[0.1382408, 0.7718657, 10. , 10. ],
              [ 10. , 0.5595833, 0.83703255, 10. ],
              [ 10. , 10. , 0.79473842, 0.91882331]])
h.flat[[2,3,4,7,8,9]] = 10
ibound = np.array([[0, 1, 1, 0],
                   [0, 1, 1, 0],
                   [0, 1, 0, 1]])
L1TopOld = np.array([[ 5., 5., 5., 5.],
                     [ 5., 5., 5., 5.],
                     [ 5., 5., 5., 5.]])

Create a logical mask with your condition:

mask = (ibound == 1) & (abs(h - nodata) <= eps)

Modify the array using boolean indexing

h[mask] = L1TopOld[mask]
print h

[[  0.1382408    0.7718657    5.          10.        ]
 [ 10.           0.5595833    0.83703255  10.        ]
 [ 10.           5.           0.79473842   0.91882331]]

Upvotes: 1

DSM
DSM

Reputation: 352959

You can use np.where. First, let's make some toy data (BTW, it helps if you do this part of it yourself):

>>> h = np.random.random((3,4))
>>> nodata = 10
>>> h.flat[[2,3,4,7,8,9]] = 10
>>> ibound = np.random.randint(0,2,(3,4))
>>> L1TopOld = np.ones((3,4))*5
>>> h
array([[  0.1382408 ,   0.7718657 ,  10.        ,  10.        ],
       [ 10.        ,   0.5595833 ,   0.83703255,  10.        ],
       [ 10.        ,  10.        ,   0.79473842,   0.91882331]])
>>> ibound
array([[0, 1, 1, 0],
       [0, 1, 1, 0],
       [0, 1, 0, 1]])
>>> L1TopOld
array([[ 5.,  5.,  5.,  5.],
       [ 5.,  5.,  5.,  5.],
       [ 5.,  5.,  5.,  5.]])
>>> eps = 0.01

Now we can decide which ones we want to patch:

>>> ibound & (abs(h-nodata) <= eps)
array([[0, 0, 1, 0],
       [0, 0, 0, 0],
       [0, 1, 0, 0]])

and use this to tell np.where where we want to switch:

>>> np.where(ibound & (abs(h - nodata) <= eps), L1TopOld, h)
array([[  0.1382408 ,   0.7718657 ,   5.        ,  10.        ],
       [ 10.        ,   0.5595833 ,   0.83703255,  10.        ],
       [ 10.        ,   5.        ,   0.79473842,   0.91882331]])

As pointed out in the comments, this assumes that ibound is a mask consisting only of 0 and 1. If you really only want to change the cases where ibound == 1 (and not 2, for example), that's easy too:

>>> np.where((ibound == 1) & (abs(h - nodata) <= eps), L1TopOld, h)
array([[  0.1382408 ,   0.7718657 ,   5.        ,  10.        ],
       [ 10.        ,   0.5595833 ,   0.83703255,  10.        ],
       [ 10.        ,   5.        ,   0.79473842,   0.91882331]])

(which gives the same answer here because 0 and 1 were the only numbers in ibound.)

Upvotes: 2

Related Questions