2012-08-13 110 views
6

我正在使用多维Numpy数组。使用其他索引数组访问这些数组时,我注意到了一些不一致的行为。例如:Numpy多维数组索引交换轴顺序

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 

在这个例子中,我得到的结果是:

a: (7, 5, 2) 
b: (2, 5) 
c: (5, 2) 

这混淆了我。为什么“b”和“c”不具有相同的尺寸?为什么“b”交换轴顺序,而不是“a”?

我已经能够设计这些归功于大量的单元测试的不一致在我的代码,但了解正在发生的事情,将不胜感激。

仅供参考,我使用Python 2.7.3和Numpy 1.6.2通过MacPorts。

回答

8

在语法上,这看起来像一个矛盾,但语义,你在这里做了两个非常不同的事情。在您定义的ab中,您正在做advanced indexing,有时称为fancy indexing,它返回数据的副本。在c的定义中,您正在执行basic slicing,它返回数据的视图。

为了区分它,有助于理解索引如何传递给python对象。下面是一些例子:

>>> class ShowIndex(object): 
...  def __getitem__(self, index): 
...   print index 
... 
>>> ShowIndex()[:,:] 
(slice(None, None, None), slice(None, None, None)) 
>>> ShowIndex()[...,:] 
(Ellipsis, slice(None, None, None)) 
>>> ShowIndex()[0:5:2,::-1] 
(slice(0, 5, 2), slice(None, None, -1)) 
>>> ShowIndex()[0:5:2,np.arange(3)] 
(slice(0, 5, 2), array([0, 1, 2])) 
>>> ShowIndex()[0:5:2] 
slice(0, 5, 2) 
>>> ShowIndex()[5, 5] 
(5, 5) 
>>> ShowIndex()[5] 
5 
>>> ShowIndex()[np.arange(3)] 
[0 1 2] 

正如你可以看到,有很多不同可能的配置。首先,可以传递单个项目,或者可以传递项目的元组。其次,元组可以包含slice对象,Ellipsis对象,普通整数或numpy数组。当传递对象像intslice,或Ellipsis对象或None(这是一样的numpy.newaxis

基本切片被激活。这些可以单独或以元组形式传递。下面介绍一下文档不得不说一下如何基本切片被激活:

当obj是切片对象发生

基本切片(由开始建造:停止:括号内的步骤符号),整数,或元组切片对象和整数。省略号和newaxis对象也可以与这些散布在一起。为了保持向后兼容的数字共同使用,基本切片也发起如果选择对象是任何序列(如列表)含有切片对象,省略号对象,或newaxis对象,但没有整数阵列或其它嵌入序列。当你经过一个numpy阵列,仅包含整数或任何种类的含子序列,或含有一个阵列或子序列中的元组的非元组序列

高级索引被激活。

有关如何先进的索引和基本切片不同,请参阅该文档的细节(与上文)。但在这种特殊情况下,我很清楚发生了什么事情。它与使用部分索引时的以下行为有关:

部分索引的规则是结果的形状(或设置中要使用的对象的解释形状)是x将索引子空间替换为广播索引子空间。如果索引子空间彼此相邻,则广播索引空间直接替换x中的所有索引子空间。如果索引子空间是分开的(按切片对象),则广播的索引空间是第一个,后面是x的切片子空间。

在你的a定义,它采用先进的索引,您可以有效地传递顺序[0, 1]中的元组的第三个项目,因为没有广播发生(因为没有其他序列),一切都发生如预期。

在你的b定义,也采用了先进的索引,则有效地传递序列,[0],第一项(其被转换成intp阵列),和[0, 1],第三项。这两个项目一起播放,结果与第三个项目具有相同的形状。然而,自广播发生以来,我们面临着一个问题:我们在新形状元组中插入广播形状的位置?正如文档所说,

没有明确的地方放置在索引子空间中,因此它被固定在开头。

因此,广播产生的2被移动到形状元组的开头,产生明显的转置。

+0

感谢您的详细解释。这非常有帮助。切片+广播索引造成的奇怪行为仍然令人意想不到,难以编码。例如:start [0,:,np.arange(2)] = np.ones((5,2))似乎是合法的,但由于轴重新排序,它不是 – gbarter 2012-08-13 23:52:38

+0

@gbarter为了保持原来的形状,你必须使用切片。即,这将工作:'开始[:1,:,np.arange(2)] = np.ones((5,2))' – jorgeca 2012-08-14 10:11:58