This section discusses pseudo-indices, which allow arrays to have their shapes
modified by adding axes, sometimes only for the duration of the evaluation of a
Python expression.
Consider multiplication of a rank-1 array by a scalar:
>>> a = array([1,2,3])
>>> print a * 2
[2 4 6]
This should be trivial by now; we've just multiplied a rank-1 array by a
scalar . The scalar was converted to a rank-0 array which was then broadcast to
the next rank. This works for adding some two rank-1 arrays as well:
>>> print a
[1 2 3]
>>> a + array([4])
[5 6 7]
but it won't work if either of the two rank-1 arrays have non-matching
dimensions which aren't 1. In other words, broadcast only works for
dimensions which are either missing (e.g. a lower-rank array) or for dimensions
of 1.
With this in mind, consider a classic task, matrix multiplication. Suppose we
want to multiply the row vector [10,20] by the column vector [1,2,3].
>>> a = array([10,20])
>>> b = array([1,2,3])
>>> a * b
ValueError: Arrays have incompatible shapes
This makes sense: we're trying to multiply a rank-1 array of shape (2,) with a
rank-1 array of shape (3,). This violates the laws of broadcast. What we really
want to do is make the second vector a vector of shape (3,1), so that the first
vector can be broadcast across the second axis of the second vector. One way to
do this is to use the reshape function:
>>> a.getshape()
(2,)
>>> b.getshape()
(3,)
>>> b2 = reshape(b, (3,1))
>>> print b2
[[1]
[2]
[3]]
>>> b2.getshape()
(3, 1)
>>> print a * b2 # Note: b2 * a gives the same result
[[10 20]
[20 40]
[30 60]]
This is such a common operation that a special feature was added (it turns out
to be useful in many other places as well) - the NewAxis "pseudo-index",
originally developed in the Yorick language. NewAxis is an index, just like
integers, so it is used inside of the slice brackets []. It can be thought of
as meaning "add a new axis here," in much the same ways as adding a 1 to an
array's shape adds an axis. Again, examples help clarify the situation:
>>> print b
[1 2 3]
>>> b.getshape()
(3,)
>>> c = b[:, NewAxis]
>>> print c
[[1]
[2]
[3]]
>>> c.getshape()
(3,1)
Why use such a pseudo-index over the reshape function or setshape assignments?
Often one doesn't really want a new array with a new axis, one just wants it
for an intermediate computation. Witness the array multiplication mentioned
above, without and with pseudo-indices:
>>> without = a * reshape(b, (3,1))
>>> with = a * b[:,NewAxis]
The second is much more readable (once you understand how NewAxis works), and
it's much closer to the intended meaning. Also, it's independent of the
dimensions of the array b. You might counter that using something like
reshape(b, (-1,1)) is also dimension-independent, but
it's less readable and impossible with rank-3 or higher arrays? The
NewAxis-based idiom also works nicely with higher rank arrays, and with the ...
"rubber index" mentioned earlier. Adding an axis before the last axis in an
array can be done simply with:
>>> a[...,NewAxis,:]
Note that NewAxis is a numarray object, so if you used
import numarray instead of from numarray import *, you'll
need numarray.NewAxis.