I am working with multi-dimensional Numpy arrays. I have noticed some inconsistent behavior when accessing these arrays with other index arrays. For example:
import numpy as np
start = np.zeros((7,5,3))
a = start[:,:,np.arange(2)]
b = start[0,:,np.arange(2)]
c = start[0,:,:2]
print 'a:', a.shape
print 'b:', b.shape
print 'c:', c.shape
In this example, I get the result:
a: (7, 5, 2)
b: (2, 5)
c: (5, 2)
This confuses me. Why do “b” and “c” not have the same dimensions? Why does “b” swap the axis order, but not “a”?
I have been able to design my code around these inconsistencies thanks to lots of unit tests, but understanding what is going on would be appreciated.
For reference, I am using Python 2.7.3, and Numpy 1.6.2 via MacPorts.
Syntactically, this looks like an inconsistency, but semantically, you’re doing two very different things here. In your definition of
aandb, you’re doing advanced indexing, sometimes called fancy indexing, which returns a copy of the data. In your definition ofc, you’re doing basic slicing, which returns a view of the data.To tell the difference, it helps to understand how indices are passed to python objects. Here are some examples:
As you can see, there are many different possible configurations. First, individual items may be passed, or tuples of items may be passed. Second, the tuples may contain
sliceobjects,Ellipsisobjects, plain integers, ornumpyarrays.Basic slicing is activated when you pass only objects like
int,slice, orEllipsisobjects, orNone(which is the same asnumpy.newaxis). These can be passed singly or in a tuple. Here’s what the docs have to say about how basic slicing is activated:Advanced indexing is activated when you pass a
numpyarray, a non-tuple sequence containing only integers or containing subsequences of any kind, or a tuple containing an array or subsequence.For details on how advanced indexing and basic slicing differ, see the docs (linked to above). But in this particular case, it’s clear to me what’s happening. It has to do with the following behavior when using partial indexing:
In your definition of
a, which uses advanced indexing, you effectively pass the sequence[0, 1]in as the third item of the tuple, and since no broadcasting happens (because there is no other sequence), everything happens as expected.In your definition of
b, also using advanced indexing, you effectively pass two sequences,[0], the first item (which is converted into anintparray), and[0, 1], the third item. These two items are broadcast together, and the result has the same shape as the third item. However, since broadcasting has happened, we’re faced with a problem: where in the new shape tuple do we insert the broadcasted shape? As the docs say,So the
2that results from broadcasting is moved to the beginning of the shape tuple, producing an apparent transposition.