4.7 Slicing Arrays

The standard rules of Python slicing apply to arrays, on a per-dimension basis. Assuming a 3x3 array:

>>> a = reshape(arange(9),(3,3))
>>> print a
[[0 1 2]
 [3 4 5]
 [6 7 8]]
The plain [:] operator slices from beginning to end:
>>> print a[:,:]
[[0 1 2]
 [3 4 5]
 [6 7 8]]
In other words, [:] with no arguments is the same as [:] for lists -- it can be read ``all indices along this axis''. (Actually, there is an important distinction; see below.) So, to get the second row along the second dimension:
>>> print a[:,1]
[1 4 7]
Note that what was a ``column'' vector is now a ``row'' vector any ``integer slice'' (as in the 1 in the example above) results in a returned array with rank one less than the input array. There is one important distinction between slicing arrays and slicing standard Python sequence objects. A slice of a list is a new copy of that subset of the list; a slice of an array is just a view into the data of the first array. To force a copy, you can use the copy method. For example:
>>> a = arange (20)
>>> b = a[3:8]
>>> c = a[3:8].copy()
>>> a[5] = -99
>>> print b
[  3   4 -99   6   7]
>>> print c
[3 4 5 6 7]
If one does not specify as many slices as there are dimensions in an array, then the remaining slices are assumed to be ``all''. If A is a rank-3 array, then
A[1] == A[1,:] == A[1,:,:]
An additional slice notation for arrays which does not exist for Python lists (before Python 2.3), i. e. the optional third argument, meaning the ``step size'', also called stride or increment. Its default value is 1, meaning return every element in the specified range. Alternate values allow one to skip some of the elements in the slice:
>>> a = arange(12)
>>> print a
[ 0  1  2  3  4  5  6  7  8  9 10 11]
>>> print a[::2]                        # return every *other* element
[ 0  2  4  6  8 10]
Negative strides are allowed as long as the starting index is greater than the stopping index:
>>> a = reshape(arange(9),(3,3))                                                                                          Array Basics
>>> print a
[[0 1 2]
 [3 4 5]
 [6 7 8]]
>>> print a[:, 0]
[0 3 6]
>>> print a[0:3, 0]
[0 3 6]
>>> print a[2::-1, 0]
[6 3 0]
If a negative stride is specified and the starting or stopping indices are omitted, they default to ``end of axis'' and ``beginning of axis'' respectively. Thus, the following two statements are equivalent for the array given:
>>> print a[2::-1, 0]
[6 3 0]
>>> print a[::-1, 0]
[6 3 0]
>>> print a[::-1]                       # this reverses only the first axis
[[6 7 8]
 [3 4 5]
 [0 1 2]]
>>> print a[::-1,::-1]                  # this reverses both axes
[[8 7 6]
 [5 4 3]
 [2 1 0]]
One final way of slicing arrays is with the keyword "..." This keyword is somewhat complicated. It stands for ``however many `:' I need depending on the rank of the object I'm indexing, so that the indices I do specify are at the end of the index list as opposed to the usual beginning''.

So, if one has a rank-3 array A, then A[...,0] is the same thing as A[:,:,0], but if B is rank-4, then B[...,0] is the same thing as: B[:,:,:,0]. Only one "..." is expanded in an index expression, so if one has a rank-5 array C, then C[...,0,...] is the same thing as C[:,:,:,0,:].

When assigment source and destination locations overlap, i.e. when an array is assigned onto itself at overlapping indices, it may produce something "surprising":

>>> n=numarray.arange(36)
>>> n[11:18]=n[7:14]
>>> n
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10,  7,  8,  9, 10,  7,
        8,  9, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
       32, 33, 34, 35])

If the slice on the right hand side (RHS) is AFTER that on the left hand side (LHS) for 1-D array, then it works fine:

>>> n=numarray.arange(36)
>>> n[1:8]=n[7:14]       
>>> n
array([ 0,  7,  8,  9, 10, 11, 12, 13,  8,  9, 10, 11, 12, 13, 14, 15,
       16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
       32, 33, 34, 35])

Actually, this behavior can be undedrstood if we follow the pixel by pixel copying logic. Parts of the slice start to get the "updated" values when the RHS is before the LHS.

An easy solution which is guaranteed to work is to use the copy() method on the righ hand side:

>>> n=numarray.arange(36)
>>> n[11:18]=n[7:14].copy()
>>> n
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10,  7,  8,  9, 10, 11,
       12, 13, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
       32, 33, 34, 35])

Send comments to the NumArray community.