Views and copies confusion with NumPy arrays when combining index operations

Viewed 540
>>> a = np.arange(12).reshape(3,4)

>>> a[0:3:2, :][:, [0,2]] = 100   ### the first time
>>> a  
array([[100, 1, 100, 3],
[ 4, 5, 6, 7],
[100, 9, 100, 11]])

>>> a[:, [0, 2]][0:3:2, :] = 0    ### second time
>>> a   
array([[100, 1, 100, 3],
       [ 4, 5, 6, 7],
       [100, 9, 100, 11]])

I am really confused about views and copies in python. The code above shows that the first time the given rows and columns in array a was changed to 100, which changed the original array a.

However, the second time the original array was not changed. Why is that?

1 Answers

A lookup with [:, [0,2]] will return a copy because it's advanced indexing. However when you assign to a slice (e.g. array[whatever] = sth) it won't create the copy but assign to the specified items, even if it's advanced indexing.

So the first example works because the first slicing returns a view and then it uses assignment for the slice of the view.

However the second one "fails" because you assign to a slice of a copy (because the advanced indexing is done before the regular indexing).

The difference is mainly because another method is responsible for setting (__setitem__) to slices than for getting (__getitem__) these slices. To disassemble your statements:

a[0:3:2, :][:, [0,2]] = 100 

a.__getitem__((slice(0, 3, 2), slice(None))).__setitem__((slice(None), [0, 2]), 100)
|-------------- returns a view ------------|

while the second one would be:

a[:, [0,2]][0:3:2, :] = 0

a.__getitem__((slice(None), [0, 2])).__setitem__((slice(0, 3, 2), slice(None)), 0)
|--------- returns a copy ---------|
Related