意外的numpy数组索引行为

3

当执行特定的切片时,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.4和numpy 1.15.1。

这是一种混合基本和高级索引的情况(请参阅索引文档页面)。当一个切片(或省略号)在其他索引的中间时,它会被移动到结果的末尾。还有重复的SO。你已经找到了解决方法。 - hpaulj
https://dev59.com/9rPma4cB1Zd3GeqPqnKY - hpaulj
1个回答

2
你在最后两种情况中发现的结果令人意外是因为数组的索引遵循高级索引的规则,尽管你也使用了切片进行索引。
如果想要深入了解背后的原理,请查看组合高级和基本索引。在这些最后的情况中,你会得到意外的结果形状。在文档中,你会看到其中一个可能导致出现意外结果的情况是:
高级索引之间由切片、省略号或新轴分隔。例如: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

这种行为背后的原因是什么?

需要记住的一般规则是:

由数组索引引入的结果轴位于前面,除非它们是连续的。

因此,由于这里的索引数组不是连续的,它们被用于的结果轴将出现在前面,而切片的维度将出现在后面。

虽然使用1维数组进行索引似乎非常奇怪,但请注意,也可以使用任意数量维度的数组进行索引。假设我们使用3d数组在第一个和最后一个轴上同时对同一示例数组进行索引,两个数组的形状都为(3,4,2)。因此,我们知道最终的数组也将具有形状(3,4,2),因为两个索引数组广播到相同的形状。现在问题是,在索引数组之间取完整切片应该放在哪里?

考虑到不再清楚它应该放在中间,因此在这些情况下有一个惯例,即切片维度放在末尾。因此,在这种情况下,我们的任务是重新排列数组的维度以匹配预期的输出。在上面的示例中,我们可以交换最后两个轴,并使用swapaxes来按预期排列维度。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接