Home > Enterprise >  Replacing elements in ndarray based on condition
Replacing elements in ndarray based on condition

Time:06-17

I have bi-dimensional array, like that:

f_vars = np.array([[0,4],
                   [0,2],
                   [3,-1],
                   [3,4],
                   [1,-1]])

I am trying to replace some of the elements based on few conditions, as follow: if an element is equal to -1: leave it as is, if it is less then given variable (for example 2): increase by 1. If it is equal to 2, make it 0. If it is between two given variables (for example 2 and 4), leave it as is. If it is equal to the second variable, make it 0. If it is higher than the second variable: decrease with one.

So far I have tried the following cycle:

for i in np.nditer(f_vars):
    if i < 2: f_vars[i] = f_vars[i] 1
print(f_vars)

This is only the beginning of my cycle, but the result is quite unexpected:

[[ 3  7]
 [ 0  2]
 [ 3 -1]
 [ 3  4]
 [ 2  0]]

It is modifying only the first and the last element for some reason, and the modification is not by adding 1, but quite different.

Any advice will be highly appreciated.

CodePudding user response:

Using logical indexing:

f_vars[f_vars < 2]  = 1

will give you:

[[1 4]
 [1 2]
 [3 0]
 [3 4]
 [2 0]]

as expected. You can continue in the same manner for applying more conditionals. You might make use of np.logical_and to achieve multiple conditions. Take care of the order you apply the conditions and if you find it confusing, an if-elif-else statement would be the easiest. The np.nditer indexing is done like this:

for x in np.nditer(f_vars,op_flags = ['readwrite']):
    if x == -1:
        continue
    elif x < 2: 
        x[...]  = 1

So, you have to set op_flags = ['readwrite'] and index through the i[...] syntax.

CodePudding user response:

There will be some ways you can use masked arrays together. Let work on when you want to find values that are equal to -1. So, mask will be as:

mask_equal_neg1 = f_vars == -1
# [[False False]
#  [False False]
#  [False  True]
#  [False False]
#  [False  True]]

If you want the True indices be unchanged, so one way is to use ~ before mask_equal_neg1 or np.invert to invert the Booleans to adjust it for indices you want change:

~mask_equal_neg1
# [[ True  True]
#  [ True  True]
#  [ True False]
#  [ True  True]
#  [ True False]]

f_vars[mask_equal_neg1]
# [-1 -1]

f_vars[~mask_equal_neg1]
# [0 4 0 2 3 3 4 1]

For using these masks in combination, you can use & (and) or | (or):

mask_equal_neg1 & ~mask_equal_neg1
# [[False False]
#  [False False]
#  [False False]
#  [False False]
#  [False False]]

f_vars[mask_equal_neg1 & ~mask_equal_neg1]
# []

mask_equal_neg1 | ~mask_equal_neg1
# [[ True  True]
#  [ True  True]
#  [ True  True]
#  [ True  True]
#  [ True  True]]

f_vars[mask_equal_neg1 | ~mask_equal_neg1]
# [ 0  4  0  2  3 -1  3  4  1 -1]

In the above code, we get all True or False because we used two masks which are inverse to each other.
The total masks needed are as follows:

mask_equal_neg1 = f_vars == -1
mask_lower_2 = f_vars < 2
mask_equal_2 = f_vars == 2
mask_bigger_2 = f_vars > 2
mask_lower_4 = f_vars < 4
mask_equal_columns = f_vars[:, 0] == f_vars[:, 1]
mask_equal_bigger_column = f_vars[:, 0] > f_vars[:, 1]

If we want to use masks repeatedly for in_place replacements, so we must consider their using order i.e. the masks must be used in order of the needs.

Now, in your case assuming indices with value equal to -1 will be never changed, so we use its inverse in combination with the other masks. For the next step we want to find values <2 (step 2) and add by 1. Since, the next step (step 3) that is to find values equal 2 will have conflict with the previous condition (i.e. <2 step 2) when we faced to values =1 (if we have values =1 in step 2, so those values will be add by 1 and became 2; the 3rd step will works on this value), we must consider the order of using masks. If we have:

f_vars = np.array([[0, 4],
                   [0, 2],
                   [3, -1],
                   [3, 1],
                   [1, -1]])

Now consider order in 2 various conditions:

  1. step 1 then step 2
mask_equal_neg1 = f_vars == -1
mask_lower_2 = f_vars < 2
f_vars[~mask_equal_neg1 & mask_lower_2]  = 1
# [[ 1  4]   <-- column 1
#  [ 1  2]   <-- column 1
#  [ 3 -1]
#  [ 3  2]   <-- column 2
#  [ 2 -1]]  <-- column 1

mask_equal_2 = f_vars == 2
f_vars[~mask_equal_neg1 & mask_equal_2] = 0
# [[ 1  4]
#  [ 1  0]   <-- column 2
#  [ 3 -1]
#  [ 3  0]   <-- column 2
#  [ 0 -1]]  <-- column 1
  1. step 2 then step 1
mask_equal_neg1 = f_vars == -1
mask_equal_2 = f_vars == 2
f_vars[~mask_equal_neg1 & mask_equal_2] = 0
# [[ 0  4]
#  [ 0  0]   <-- column 2
#  [ 3 -1]
#  [ 3  1]
#  [ 1 -1]]

mask_lower_2 = f_vars < 2
f_vars[~mask_equal_neg1 & mask_lower_2]  = 1
# [[ 1  4]   <-- column 1
#  [ 1  1]   <-- column 1 & column 2
#  [ 3 -1]
#  [ 3  2]   <-- column 2
#  [ 2 -1]]  <-- column 1

assuming we do the first one (i.e. step 1 then step 2), so we continue from the modified array:

f_vars = np.array([[1, 4],
                   [1, 0],
                   [3, -1],
                   [3, 0],
                   [0, -1]])

Next step is to avoid changing the values between 2 and 4, so we need the invert Boolean array:

~mask_equal_neg1 & ~mask_bigger_2 | ~mask_lower_4
# [[ True  True]
#  [ True  True]
#  [False False]
#  [False  True]
#  [ True False]]

f_vars[~mask_equal_neg1 & ~mask_bigger_2 | ~mask_lower_4]
# [1 4 1 0 0 0]

Now we can go to the last two conditions which need to consider the needed order again. If we want to change values equal to the second variable to 0 and then decrease with one the values higher than the second variable, so if:

f_vars = np.array([[1, 1],
                   [1, 0],
                   [3, -1],
                   [3, 0],
                   [0, -1]])

so, we can specify the mask for rows which satisfy the condition and modify the first column (index --> 0):

mask_equal_columns = f_vars[:, 0] == f_vars[:, 1]
# [ True False False False False]
f_vars[mask_equal_columns, 0] = 0
# [[ 0  1]   <-- column 1
#  [ 1  0]
#  [ 3 -1]
#  [ 3  0]
#  [ 0 -1]]

mask_equal_bigger_column = f_vars[:, 0] > f_vars[:, 1]
# [False  True  True  True  True]
f_vars[mask_equal_bigger_column, 0] -= 1
# [[ 0  1]
#  [ 0  0]   <-- column 1
#  [ 2 -1]   <-- column 1
#  [ 2  0]   <-- column 1
#  [-1 -1]]  <-- column 1
  • Related