4.8 Index Arrays

Arrays used as subscripts have special meanings which implicitly invoke the functions put (page ), take (page ), or compress (page ). If the array is of Bool type, then the indexing will be treated as the equivalent of the compress function. If the array is of an integer type, then a take or put operation is implied. We will generalize the existing take and put as follows: If ind1, ind2, ... indN are index arrays (arrays of integers whose values indicate the index into another array), then x[ind1, ind2] forms a new array with the same shape as ind1, ind2 (they all must be broadcastable to the same shape) and values such: "result[i,j,k] = x[ind1[i,j,k], ind2[i,j,k]]" In this example, ind1, ind2 are index arrays with 3 dimensions (but they could have an arbitrary number of dimensions). To illustrate with some specific examples:

>>> # simple index array example
>>> x = 2*arange(10)
>>> ind = array([3,6,2,4,4])
>>> x[ind]
array([ 6, 12,  4,  8,  8])
>>> # index a 2-d array
>>> x = arange(12)
>>> x.setshape((3,4))
>>> x
array([[ 0,  1,  2,  3],
  [ 4,  5,  6,  7],
  [ 8,  9, 10, 11]])
>>> ind1 = array([2,1])
>>> ind2 = array([0,2])
>>> x[ind1, ind2]
array([8, 6])
>>> # multidimensional index arrays
>>> ind1 = array([[2,2],[1,0]])
>>> ind2 = array([[2,1],[0,1]])
>>> x[ind1, ind2]
array([[10,  9],
  [ 4,  1]])
>>> # Mindblowing combination of multidimensional index arrays with
>>> # partial indexing. Strap on your seatbelts.
>>> x[ind1]
array([[[ 8,  9, 10, 11],
  [ 8,  9, 10, 11]],
  [[ 4,  5,  6,  7],
   [ 0,  1,  2,  3]]])
Note that in this last example, each index in the single index array (ind1) is treated as though x were given only one index. For each of these ``single'' indices, a 1-d array is returned, thus the combination of the 2 dimensions in the index array combined with the leftover dimension in the array being indexed produces a 3 dimensional array.

When using constants for some of the index positions, then the result uses that constant for all values. Slices and strides (at least initially) will not be permitted in the same subscript as index arrays. So

>>> x[ind1, 2]
array([[10, 10],
  [ 6,  2]])
would be legal, but
>>> x[ind1, 1:3]
Traceback (most recent call last):
[...]
    raise IndexError("Cannot mix arrays and slices as indices")
IndexError: Cannot mix arrays and slices as indices
would not be. Similarly for assignment:
x[ind1, ind2, ind3] = values
will form a new array such that:
x[ind1[i,j,k], ind2[i,j,k], ind3[i,j,k]] = values[i,j,k]

The index arrays and the value array must be broadcast consistently. (As an example: ind1.setshape((5,4)), ind2.setshape((5,)), ind3.setshape((1,4)), and values.setshape((1,)).)

# Index put example, using broadcasting and illustrating that Python
# integer sequences work as indices also.
>>> x = zeros((10,10))
>>> x[[2,5,6],array([0,1,9,3])[:,NewAxis]] = 111
>>> x
array([[  0,   0,   0,   0,   0,   0,   0,   0,   0,   0],
  [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0],
  [111, 111,   0, 111,   0,   0,   0,   0,   0, 111],
  [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0],
  [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0],
  [111, 111,   0, 111,   0,   0,   0,   0,   0, 111],
  [111, 111,   0, 111,   0,   0,   0,   0,   0, 111],
  [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0],
  [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0],
  [  0,   0,   0,   0,   0,   0,   0,   0,   0,   0]])
If indices are repeated, the last value encountered will be stored. When index values are out of range they will be clipped to the appropriate range. That is to say, an index that is too large will be taken to refer to the last element, and a negative index will be interpreted as zero (future versions will interpret negative indices in the usual Python style, counting backwards from the end). Use of the equivalent  take and  put functions will allow other interpretations of the indices (raise exceptions for out of bounds indices, allow negative indices to work backwards as they do when used individually, or for indices to wrap around). The same behavior applies for functions such as choose and where.
>>> x = 2*arange(10)
>>> x[[0, 5, 100, 5, -2]] = [1000, 1005, 1100, 2005, 3005]
>>> x
array([3005,    2,    4,    6,    8, 2005,   12,   14,   16, 1100])

Send comments to the NumArray community.