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