Today, when I used the slice operator to modify an array, I found that the following operations got different results, although operations seemed to be the same.
import numpy as np
a = np.array([[1,2,3],[0,5,4],[4,5,3],[5,8,7]])
print(a)
a[[0,1],:][0:2,:] = [[11,12,13],[8,9,7]]
print(a)
a[0:2,:][[0,1],:] = [[11,12,13],[8,9,7]]
print(a)
the results are following:
[[1 2 3]
[0 5 4]
[4 5 3]
[5 8 7]]
[[1 2 3]
[0 5 4]
[4 5 3]
[5 8 7]]
[[11 12 13]
[8 9 7]
[4 5 3]
[5 8 7]]
I guess that "a[[0,1],:]" creates a new array, and "a[0:2,:]" doesn't. But why? I am confused. I want to know under what conditions a new array will be created when I use the slice operator.
CodePudding user response:
a[[0,1],:]
with a list is "advanced indexing". It makes a copy. The [0,1]
is not a slice.
a[0:2,:]
with a slice is "basic indexing". It returns a view
.
a[[0,1],:][0:2,:] = ...
tries to modify that copy, and doesn't affect a
. a[0:2,:][[0,1],:] = ...
tries to modify the view
, and thus also modifies a
.
a[[0,1],:] = ...
will successfully modify a
.
It's a good idea of avoid the the indexing sequence when setting values. a[...][...] = ...
. As this example shows, sometimes it works, other times not. If you avoid it entirely you won't have to worry about which works.
Even when fetching values the double indexing is less than ideal. It always works, but can be harder to understand.
a[0:2,:][[0,1],:] = [[11,12,13],[8,9,7]]
is evaluated as
a.__getitem__((slice(0,2), slice(None))).
__setitem__(([0,1], slice(None)), [[11,12,13],[8,9,7] )
The getitem
is performed first, and the setitem
is applied to that result. So when chaining indexing it is crucial that you understand what that first indexing does.