This is a knowledge topic based on an earlier question I had today. These are some odd inconsistencies in the behavior of numpy I’ve witnessed.
First, if you run this code:
A = ones((10,4))
view = A[:,1]
view.fill(7)
A
This will change the 2nd column to all 7s as arrays are indexed from 0 and slices are simply views of the same matrix. Awesome, that’s exactly what I want to happen.
Now, if you run this:
A = ones((10,4))
view = A[:,1:2]
view.fill(7)
A
It’ll have the same effects as the first example. Why is it that a:b specifies the columns from a to b-1? Is there a specific reason for this in the language? It seems that if I input say, 1:3, that should give me columns 1, 2, and 3 – not 1 and 2.
Finally, if you run this:
A = ones((10,4))
view = A[:,(1,2)]
view.fill(7)
A
There’s no side effects on A. It looks like if you create a view using a tuple, it somehow doesn’t correctly propagate any further side effects on the original matrix. Any insights?
This is a Python convention. The same is true for ordinary lists and
range(a, b)will return a list containing the numbersaup to and includingb-1, but notb. The benefit of this convention is that slicing bya:bwhereaandbare numbers will returnb-aelements/rows/columns instead of the more complicateda-b+1.This is a Numpy idiosyncrasy, caused by the fact that it can only create views based on slices; these can be efficiently implemented, while tuple-based slices cannot. You can simulate this behavior with the following snippet, which shows what Python index syntax does under the hood:
Now try
So
A[1:2]is shorthand forA.__getitem__(slice(1, 2, None))whileA[1:2] = 'ham'is shorthand forA.__setitem__(slice(1, 2, None), 'ham'). Since there’s really two distinct methods involved, the behavior of slicing can be very different depending on whether it’s part of an assignment statement. In the Numpy case, there’s a subtle interplay between this difference and that betweensliceandtupleobjects.