切片 numpy 数组时得到意想不到的形状

Getting unexpected shape while slicing a numpy array

我有一个 4D numpy 数组。在单个维度中对多个索引进行切片时,我的轴会互换。我是不是漏掉了一些微不足道的东西。

import numpy as np
from smartprint import smartprint as prints

a = np.random.rand(50, 60, 70, 80)
b = a[:, :, :, [2,3,4]]
prints (b.shape)  # this works as expected 

c = a[1, :, :, [2,3,4]]
prints (c.shape)  # here, I see the axes are interchanged

输出:

b.shape : (50, 60, 70, 3)
c.shape : (3, 60, 70)

以下是一些可能有助于解释问题的观察结果。

从 3d 数组开始,预期步幅:

In [158]: x=np.arange(24).reshape(2,3,4)
In [159]: x.shape,x.strides
Out[159]: ((2, 3, 4), (48, 16, 4))

最后一个轴上的高级索引:

In [160]: y=x[:,:,[0,1,2,3]]    
In [161]: y.shape, y.strides
Out[161]: ((2, 3, 4), (12, 4, 24))

请注意,步幅不是正常的 C-contiguous 顺序。对于二维数组,我们将其描述为 F-contiguous。这是一个模糊的索引细节,通常无关紧要。

显然,在执行此索引时,它首先创建一个数组,其中最后一个索引维度在第一位:

In [162]: y.base.shape
Out[162]: (4, 2, 3)
In [163]: y.base.strides
Out[163]: (24, 12, 4)

y 是这个交换轴的基础,它的基础的 view

中间有一片的情况是

In [164]: z=x[1,:,[0,1,2,3]]
In [165]: z.shape, z.strides
Out[165]: ((4, 3), (12, 4))

In [166]: z.base    # its own base, not a view

z 转换为预期的 (3,4) 形状会将步幅切换为 (4,12),F-contiguous.

通过两步索引,我们得到了一个具有预期形状的数组,但 F 跨步了。它的 base 看起来很像 z.

In [167]: w=x[1][:,[0,1,2,3]]

In [168]: w.shape, w.strides
Out[168]: ((3, 4), (4, 12))

In [169]: w.base.shape, w.base.strides
Out[169]: ((4, 3), (12, 4))

文档通过说明在中间使用切片执行高级索引时存在歧义来证明轴中的切换是合理的。使用 (2,1) 和 (4,) 索引时可能最清楚:

In [171]: w=x[[[0],[1]],:,[0,1,2,3]]

In [172]: w.shape, w.strides
Out[172]: ((2, 4, 3), (48, 12, 4))

中间尺寸为 3 号,“缝在最后”。对于 x[1,:,[0,1,2,3]],模糊性参数不是那么好,但显然它使用的是相同的索引方法。当在 github 问题中提出这个问题时,声称重新编制索引以纠正这个问题太困难了。个别情况可能会改正,但全面改就太复杂了。

这个维度切换似乎每年在 SO 上出现几次,这很烦人,但不是关键问题。