意外行为numpy数组索引

时间:2019-05-24 20:34:12

标签: python numpy numpy-ndarray

以某种意想不到的方式执行特定切片时,numpy数组的形状正在发生变化

我尝试了几种将同一数组切片的方法,但是细微的差异会导致数组形状的结果不同

import numpy as np
z = np.zeros((1,9,10,2))

# This makes sense
print(z[...,[1,0]].shape)
# (1, 9, 10, 2)
print(z[0,...].shape)
# (9, 10, 2)
print(z[0:1,...,[1,0]].shape)
# (1, 9, 10, 2)
print(z[0][...,[1,0]].shape)
# (9, 10, 2)

# This doesn't, I would expect (9, 10, 2) in both cases
print(z[0,:,:,[1,0]].shape)
# (2, 9, 10)
print(z[0,...,[1,0]].shape)
# (2, 9, 10)

在最后两个示例中,我不明白为什么最后一个轴移动到第一个位置。

我将Python 3.6.4numpy 1.15.1一起使用

1 个答案:

答案 0 :(得分:1)

在最后两种情况下可能会发现结果出乎意料的原因是,即使您也使用切片索引,但数组的索引遵循advanced indexing的规则。

有关此行为的详细解释,请检查combining advanced and basic indexing。在这些最后的情况下,您会得到意想不到的结果形状。在文档中,您将看到上述可能导致意外结果的场景之一是:

  • 高级索引由切片,省略号或新轴分隔。例如x[arr1, :, arr2]

在您的情况下,尽管您仅使用整数沿第一个轴进行索引,但广播该整数并将两个数组都迭代为一个。 在这种情况下,由高级索引操作产生的维数首先出现在结果数组中,然后是子空间维数。

这里的关键是要理解文档中提到的,就像将每个高级索引元素的索引结果连接起来一样。

所以从本质上讲,它的作用是:

z = np.random.random((1,9,10,2))
a = np.concatenate([z[0,:,:,[1]], z[0,:,:,[0]]], axis=0)
b = z[0,:,:,[1,0]]

np.allclose(a,b)
# True

但是...为什么会发生?

其背后的原因是高级索引和基本索引的行为不同,因为它们具有不同的用途。 让我们通过一个例子来阐明这一点:

a = np.random.randint(1,10, (3,3,4))

print(a)
array([[[8, 2, 7, 2],
        [3, 1, 2, 4],
        [9, 8, 2, 2]],

       [[4, 2, 7, 4],
        [9, 6, 6, 7],
        [4, 2, 5, 1]],

       [[7, 4, 2, 3],
        [6, 9, 3, 6],
        [3, 3, 2, 6]]])

现在说我们想索引a,以便从前两个2d数组中获取最后两个列。为此,方法是使用basic slicing

a[:2,:,2:]

array([[[7, 2],
        [2, 4],
        [2, 2]],

       [[7, 4],
        [6, 7],
        [5, 1]]])

但是,现在,如果我想做同样的事情,而不是选择最后两个列,我希望分别从两个数组中选择第一列和第二列,该怎么办?我该如何处理?为此,我们有了高级索引

a[[0,1],:,[2,3]]

array([[7, 2, 2],
       [4, 7, 1]])

因此,您可以看到两种索引编制方法在本质上是不同的:

  

整数数组索引允许根据数组的N维索引选择数组中的任意项。每个整数数组代表该维度的多个索引

使用多个高级索引时,高级索引始终会广播并迭代为一个,其中获得的结果具有以下形状:

  

result[i_1, ..., i_M] == x[ind_1[i_1, ..., i_M], ind_2[i_1, ..., i_M], ind_N[i_1, ..., i_M]]


因此,即使切片包含与数组索引相同数量的轴元素,结果形状也会有所不同。如上所述,这是因为两种索引编制方法都有不同的用途。

但是,如果只有一个高级索引,则不会发生这种情况:

a[:2,:2,[2,3]]

array([[[7, 2],
        [2, 4],
        [2, 2]],

       [[7, 4],
        [6, 7],
        [5, 1]]])

因为没有其他高级索引可用于广播,因此索引数组充当了沿最后一个轴的切片。