alberto
alberto

Reputation: 2645

Updating array values using two masks a[mask1][mask2]=value

Given an array and a mask, we can assign new values to the positions that are TRUE in the mask:

import numpy as np    
a = np.array([1,2,3,4,5,6])
mask1 = (a==2) | (a==5)
a[mask1] = 100
print a
# [  1 100   3   4 100   6]

However, if we apply a second mask over the first one, we can access to the values but we cannot modify them:

a = np.array([1,2,3,4,5,6])
mask1 = (a==2) | (a==5)
mask2 = (a[mask1]==2)
print a[mask1][mask2]
# [2]
a[mask1][mask2] = 100
print a
# [ 1 2 3 4 5 6 ]

Why does it happen?

(Even if it seems a bizarre way to do this. Just out of curiosity)

Upvotes: 4

Views: 358

Answers (2)

Alex Riley
Alex Riley

Reputation: 176840

You are chaining advanced* indexing operations for the assignment, which prevents the value 100 being written back to the original array.

a[mask1] returns a new array with a copy of the original data. Writing a[mask1][mask2] = 100 means that this new array is indexed with mask2 and the value 100 assigned to it. This leaves a unchanged.

Simply viewing the items will appear to work fine because the values you pick out from the copy a[mask1] are the values you would want from the original array (although this is still inefficient as data is copied multiple times).

*advanced (or "fancy") indexing is triggered with a boolean array or an array of indices. It always returns a new array, unlike basic indexing which returns a view onto the original data (this is triggered, for example, by slicing).

Upvotes: 3

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476709

This is probably because you mix getters and setters preventing backpropagation.

It's because you use mark1 as an indexer:

>>> mask1
array([False,  True, False, False,  True, False], dtype=bool)

now by setting a[mask1] = 100, you will set all the elements where mask1 was true thus resulting in

>>> a
array([  1, 100,   3,   4, 100,   6])

note that you have only called a "setter" so to speak on a.

Now for a[mask1][mask2] = 100 you actually call both a getter and setter. Indeed you can write this as:

temp = a[mask1] #getter
temp[mask2] = 2#setter

as a result you only set the value in the temp, and thus the value is not "backpropagated" so to speak to a itself. You should see temp as a copy (although internally it is definitely possible that a python interpreter handles it differently).

Note: note that there can be circumstances where this behavior works: if temp is for instance a view on an array, it could support backwards propagation. This page for instance shows ways to return a view instead of a copy.

Upvotes: 3

Related Questions