11

我正在使用多维 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”?

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

作为参考,我通过 MacPorts 使用 Python 2.7.3 和 Numpy 1.6.2。

4

1 回答 1

12

从句法上看,这看起来是不一致的,但从语义上看,你在这里做了两件非常不同的事情。在 and 的定义中ab您正在执行高级索引,有时称为花式索引,它返回数据的副本。在您的定义中c,您正在执行基本切片,它返回数据视图。

为了区分,它有助于理解如何将索引传递给 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数组。

当您仅int传递、sliceEllipsis对象或None(与 相同)等对象时,将激活基本切片numpy.newaxis。这些可以单独传递,也可以在一个元组中传递。以下是文档关于如何激活基本切片的内容:

当 obj 是切片对象(由括号内的 start:stop:step 表示法构造)、整数或切片对象和整数的元组时,就会发生基本切片。Ellipsis 和 newaxis 对象也可以穿插其中。为了保持向后兼容 Numeric 中的常见用法,如果选择对象是包含切片对象、Ellipsis 对象或 newaxis 对象的任何序列(例如列表),但没有整数数组或其他嵌入序列。

numpy当您传递数组、仅包含整数或包含任何类型的子序列的非元组序列或包含数组或子序列的元组时,将激活高级索引。

有关高级索引和基本切片有何不同的详细信息,请参阅文档(链接到上面)。但在这种特殊情况下,我很清楚发生了什么。使用部分索引时,它与以下行为有关:

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

a使用高级索引的定义中,您有效地将序列[0, 1]作为元组的第三项传递,并且由于没有广播发生(因为没有其他序列),所以一切都按预期发生。

在您的定义中b,也使用高级索引,您有效地传递了两个序列,[0]第一项(转换为intp数组)和[0, 1]第三项。这两项一起广播,结果与第三项具有相同的形状。然而,由于广播已经发生,我们面临一个问题:我们在新的形状元组中的什么地方插入广播的形状?正如文档所说,

索引子空间中没有明确的位置,因此它被附加到开头。

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

于 2012-08-13T23:07:33.000 回答