When youâre stuck like this, why not add in a few more print statements, just like the one at the end? And itâs never a bad idea in my experience, to break code down into smaller steps.
I havenât been able to find out how this works, actually. As @JamesParrott said, boolean indexing is âsupposedâ to give you a new array. (Which would make using it to modify an array impossible.) I suspect itâs a dedicated method thatâs somehow being called, but I canât find it. I would treat it as a bit on numpy magic. Since itâs magic, it is fragile, and using aa[idx][:] instead of aa[idx] breaks the magic.
numpy arrays are contiguous data structures in memory, this allows to return slices of them as âviewsâ on them (it equally works for strides), thus not requiring to allocate more memory. But when doing aa[idx], you are passing indexes, this forces numpy to construct a new array (with np.take under the hood), instead of creating a view on it. np.shares_memory can help you investigate whether arrays are views of others or not
As an expression x[i] is equivalent to x.__getitem__(i) which in the case of numpy arrays returns a new object. If i was a slice then the new object is still a view on to the same buffer as the original object but if i is a boolean array or an array of integer indices then it returns an array with a newly allocated buffer and a copy of the data (a shallow copy in the case of object arrays).
The statement x[i] = v is equivalent to the statement x.__setitem__(i, v) which for a numpy array (and normal Python containers) always mutates x in place whatever kind of index i is.
When you do x[i][j] = v this equivalent to (x[i])[j] = v or in other words:
x.__getitem__(i).__setitem__(j, v)`
Now if i is a slice then x.__getitem__(i) is a view into the same buffer as x. However if i is an array of bool/int then __setitem__(j, v) is being called on a temporary array that does not share its buffer with x.
A statement like x[i] *= 2 is equivalent to:
x.__setitem__(i, x.__getitem__(i) * 2)
which always mutates x. A statement like x[i][j] *= 2 is equivalent to:
tmp = x[i]
tmp[j] *= 2
The question then is if tmp and x share the same buffer which will be the case if i was a slice but not if it was a bool array.