1

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]
1
  • For clarification, ibound can have values other than 0 and 1. I tested DMS's and Taha's suggestions and both worked for me. I like DSM's suggestion slightly better because it gave me an if/then type option. Commented Jun 5, 2014 at 17:01

3 Answers 3

2

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.)

Sign up to request clarification or add additional context in comments.

3 Comments

Will this work if ibound contains ints other than zero and one?
@wwii: you're right, I assumed it was a 0/1 mask. If not, you'd have to replace ibound & with (ibound == 1) &.
I have seen numpy.where() used often in answers but don't understand when it should be used instead of conditionally modifying the original array like h[mask] = L1TopOld[mask] with boolean indexing. Any tips/ideas?
1

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]]

Comments

1

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

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.