newaxis¶
The final basic multidimensional index type is newaxis
. np.newaxis
is an
alias for None
. Both np.newaxis
and None
function identically; however,
np.newaxis
is often more explicit than None
, which may appear odd in an
index, so it is generally preferred. However, some people do use None
directly instead of np.newaxis
, so it’s important to remember that they are
the same thing.
>>> import numpy as np
>>> print(np.newaxis)
None
>>> np.newaxis is None # They are exactly the same thing
True
newaxis
, as the name suggests, adds a new axis to an array. This new axis
has size 1
. The new axis is added at the corresponding location within the
array shape. A size 1
axis neither adds nor removes any elements from the
array. Using the nested lists analogy, it essentially
adds a new “layer” to the list of lists.
>>> b = np.arange(4)
>>> b
array([0, 1, 2, 3])
>>> b[np.newaxis]
array([[0, 1, 2, 3]])
>>> b.shape
(4,)
>>> b[np.newaxis].shape
(1, 4)
Including newaxis
alongside other indices in a tuple index does
not affect which axes those indices select. You can think of the newaxis
index as inserting the new axis in-place in the index, so that the other
indices still select the same corresponding axes they would select if it
weren’t there.
Take our example array, which has shape (3, 2, 4)
:
>>> a = np.arange(24).reshape((3, 2, 4))
>>> a.shape
(3, 2, 4)
The index a[0, :2]
results in a shape of (2, 4)
: the integer index 0
removes the first axis, the slice :2
selects 2 elements from the second
axis, and the third axis is not selected at all, so it remains intact with 4
elements.
>>> a[0, :2]
array([[0, 1, 2, 3],
[4, 5, 6, 7]])
>>> a[0, :2].shape
(2, 4)
Now, observe the shape of a
when we insert newaxis
at various points
within the index a[0, :2]
:
>>> a[np.newaxis, 0, :2].shape
(1, 2, 4)
>>> a[0, np.newaxis, :2].shape
(1, 2, 4)
>>> a[0, :2, np.newaxis].shape
(2, 1, 4)
>>> a[0, :2, ..., np.newaxis].shape
(2, 4, 1)
In each case, the exact same elements are selected: 0
always targets the
first axis, and :2
always targets the second axis. The only difference is
where the size 1
axis is inserted:
>>> a[np.newaxis, 0, :2]
array([[[0, 1, 2, 3],
[4, 5, 6, 7]]])
>>> a[0, np.newaxis, :2]
array([[[0, 1, 2, 3],
[4, 5, 6, 7]]])
>>> a[0, :2, np.newaxis]
array([[[0, 1, 2, 3]],
[[4, 5, 6, 7]]])
>>> a[0, :2, ..., np.newaxis]
array([[[0],
[1],
[2],
[3]],
[[4],
[5],
[6],
[7]]])
Let’s look at each of these more closely:
a[np.newaxis, 0, :2]
: the new axis is inserted before the first axis, but the0
and:2
still index the original first and second axes. The resulting shape is(1, 2, 4)
.a[0, np.newaxis, :2]
: the new axis is inserted after the first axis, but because the0
removes this axis when it indexes it, the resulting shape is still(1, 2, 4)
(and the resulting array is the same).a[0, :2, np.newaxis]
: the new axis is inserted after the second axis, because thenewaxis
comes right after the:2
, which indexes the second axis. The resulting shape is(2, 1, 4)
. Remember that the4
in the shape corresponds to the last axis, which isn’t represented in the index at all. That’s why in this example, the4
still comes at the end of the resulting shape.a[0, :2, ..., np.newaxis]
: thenewaxis
is after an ellipsis, so the new axis is inserted at the end of the shape. The resulting shape is(2, 4, 1)
.
In general, in a tuple index, the axis that each index selects corresponds to
its position in the tuple index after removing any newaxis
indices.
Equivalently, newaxis
indices can be though of as adding new axes after
the existing axes are indexed.
A size 1
axis can always be inserted anywhere in an array’s shape without
changing the underlying elements.
An array index can include multiple instances of newaxis
(or None
). Each
will add a size 1
axis in the corresponding location.
Exercise: Can you determine the shape of this array, given that a.shape
is (3, 2, 4)
?
a[np.newaxis, 0, newaxis, :2, newaxis, ..., newaxis]
Click here to show the solution
>>> a[np.newaxis, 0, np.newaxis, :2, np.newaxis, ..., np.newaxis].shape
(1, 1, 2, 1, 4, 1)
In summary,
np.newaxis
(which is just an alias forNone
) inserts a new size1
axis in the corresponding location in the tuple index. The remaining, non-newaxis
indices in the tuple index are indexed as if thenewaxis
indices were not there.
Where newaxis
is Used¶
What we haven’t said yet is why you would want to do such a thing in the first place. One use case is to explicitly convert a 1-D vector into a 2-D matrix representing a row or column vector. For example,
>>> v = np.array([0, 1, -1])
>>> v.shape
(3,)
>>> v[np.newaxis]
array([[ 0, 1, -1]])
>>> v[np.newaxis].shape
(1, 3)
>>> v[..., np.newaxis]
array([[ 0],
[ 1],
[-1]])
>>> v[..., np.newaxis].shape
(3, 1)
v[newaxis]
inserts an axis at the beginning of the shape, making v
a (1, 3)
row vector and v[..., newaxis]
inserts an axis at the end, making it a
(3, 1)
column vector.
But the most common usage is due to broadcasting. The key idea
of broadcasting is that size 1
dimensions are not directly useful, in the
sense that they could be removed without actually changing anything about the
underlying data in the array. So they are used as a signal that that dimension
can be repeated in operations. newaxis
is therefore useful for inserting
these size 1
dimensions in situations where you want to force your data to
be repeated. For example, suppose we have the two arrays
>>> x = np.array([1, 2, 3])
>>> y = np.array([100, 200])
and suppose we want to compute an “outer” sum of x
and y
, that is, we want
to compute every combination of a + b
where a
is from x
and b
is from
y
. The key realization here is that what we want is simply to
repeat each entry of x
2 times, to correspond to each entry of y
, and
respectively repeat each entry of y
3 times, to correspond to each entry of
x
. And this is exactly the sort of thing broadcasting does! We only need to
make the shapes of x
and y
match in such a way that the broadcasting will
do that. Since we want both x
and y
to be repeated, we will need to
broadcast both arrays. We want to compute
[[ x[0] + y[0], x[0] + y[1] ],
[ x[1] + y[0], x[1] + y[1] ],
[ x[2] + y[0], x[2] + y[1] ]]
That way the first dimension of the resulting array will correspond to values
from x
, and the second dimension will correspond to values from y
, i.e.,
a[i, j]
will be x[i] + y[j]
. Thus the resulting array will have shape (3, 2)
. So to make x
(which is shape (3,)
) and y
(which is shape (2,)
)
broadcast to this, we need to make them (3, 1)
and (1, 2)
, respectively.
This can easily be done with np.newaxis
:
>>> x[:, np.newaxis].shape
(3, 1)
>>> y[np.newaxis, :].shape
(1, 2)
Once we have the desired shapes, we just perform the operation, and NumPy will do the broadcasting automatically.[1]
>>> x[:, np.newaxis] + y[np.newaxis, :]
array([[101, 201],
[102, 202],
[103, 203]])
Note: broadcasting automatically prepends size 1
dimensions, so the
y[np.newaxis, :]
operation is unnecessary.
>>> x[:, np.newaxis] + y
array([[101, 201],
[102, 202],
[103, 203]])
As we saw before, size 1
dimensions may seem redundant,
but they are not a bad thing. Not only do they allow indexing an array
uniformly, they are also very important in the way they interact with NumPy’s
broadcasting rules.
Footnotes