numpy rollaxis 如此混乱的原因是什么?

Reason why numpy rollaxis is so confusing?

numpy rollaxis 函数的行为让我感到困惑。 documentation 表示:

Roll the specified axis backwards, until it lies in a given position.

对于 start 参数:

The axis is rolled until it lies before this position.

对我来说,这已经有点不一致了。

好的,直接示例(来自文档):

>>> a = np.ones((3,4,5,6))
>>> np.rollaxis(a, 1, 4).shape
(3, 5, 6, 4)

索引 1 (4) 处的轴向后滚动,直到位于索引 4 之前。

现在,当 start 索引小于 axis 索引时,我们有这样的行为:

>>> np.rollaxis(a, 3, 1).shape
(3, 6, 4, 5)

不是在索引 1 之前在索引 3 处移动轴,而是在 1 处结束。

这是为什么?为什么轴不总是滚动到给定的 start 索引?

很多困惑源于我们人类的直觉——我们如何看待移动轴。我们可以指定滚动步数(前后 2 步),或最终形状元组中的位置,或相对于原始形状的位置。

我认为理解rollaxis的关键是关注原始形状中的槽。我能想到的最一般的说法是:

a.shape[axis]滚动到a.shape[start]

之前的位置

before 在此上下文中的含义与列表 insert() 中的相同。所以在结尾之前插入是可以的。

rollaxis的基本动作是:

axes = list(range(0, n))
axes.remove(axis)
axes.insert(start, axis)
return a.transpose(axes)

如果 axis<start,则 start-=1remove 操作。

负值得到 +=n,因此 rollaxis(a,-2,-3)np.rollaxis(a,2,1) 相同。例如a.shape[-3]==a.shape[1]。列表 insert 也允许负插入位置,但 rollaxis 没有使用该功能。

所以关键是理解 remove/insert 对动作,并理解 transpose(x).

我怀疑 rollaxistranspose 的更直观版本。它是否实现了那是另一个问题。


您建议省略 start-=1 或全面应用

省略它不会改变您的 2 个示例。它只影响 rollaxis(a,1,4) 的情况,当 axes[0,2,3] 时,axes.insert(4,1)axes.insert(3,1) 相同。 1 仍然放在最后。稍微改变一下测试:

np.rollaxis(a,1,3).shape
# (3, 5, 4, 6)   # a.shape[1](4) placed before a.shape[3](6)

没有 -=1

# transpose axes == [0, 2, 3, 1]
# (3, 5, 6, 4)  # the 4 is placed at the end, after 6

如果相反 -=1 始终适用

np.rollaxis(a,3,1).shape
#  (3, 6, 4, 5)

变成

(6, 3, 4, 5)

现在 63 之前,这是原来的 a.shape[0]。 roll 3 之后是 a.shape[1]。但这是一个不同的 roll 规范。

归结为 start 是如何定义的。是原订单中的位置,还是退货订单中的位置?


如果你更愿意将start视为最终形状中的索引位置,那么删除before部分并直接说'move axis to dest slot'不是更简单吗?

myroll(a, axis=3, dest=0) => (np.transpose(a,[3,0,1,2])
myroll(a, axis=1, dest=3) => (np.transpose(a,[0,2,3,1])

简单地删除 -=1 测试可能会成功(忽略负数和边界的处理)

def myroll(a,axis,dest):
    x=list(range(a.ndim))
    x.remove(axis)
    x.insert(dest,axis)
    return a.transpose(x)
a = np.arange(1*2*3*4*5).reshape(1,2,3,4,5)

np.rollaxis(a,axis,start)

'axis' 是要从 0 开始移动的轴的索引。在我的示例中,位置 0 处的轴是 1。

'start' 是我们希望在之前移动所选轴的轴的索引(再次从 0 开始)。

因此,如果start=2,则位置2的轴为3,因此选择的轴将在3之前。

示例:

>>> np.rollaxis(a,0,2).shape # the 1 will be before the 3.

(2, 1, 3, 4, 5)

>>> np.rollaxis(a,0,3).shape # the 1 will be before the 4.

(2, 3, 1, 4, 5)

>>> np.rollaxis(a,1,2).shape # the 2 will be before the 3.

(1, 2, 3, 4, 5)

>>> np.rollaxis(a,1,3).shape # the 2 will be before the 4.

(1, 3, 2, 4, 5)

因此,在滚动之后,滚动之前轴上的数字将被放置在滚动之前开始处的数字之前。

如果你这样想 rollaxis,它非常简单而且非常有意义,尽管他们选择这样设计它很奇怪。

那么,当 axis 和 start 相同时会发生什么?好吧,你显然不能把数字放在它自己之前,所以数字不会移动,指令变成空操作。

示例:

>>> np.rollaxis(a,1,1).shape # the 2 can't be moved to before the 2.

(1, 2, 3, 4, 5)

>>> np.rollaxis(a,2, 2).shape # the 3 can't be moved to before the 3.

(1, 2, 3, 4, 5)

把坐标轴移到最后怎么样?好吧,结束后没有数字,但你可以指定结束后开始。

示例:

>>> np.rollaxis(a,1,5).shape # the 2 will be moved to the end.

(1, 3, 4, 5, 2)

>>> np.rollaxis(a,2,5).shape # the 3 will be moved to the end.

(1, 2, 4, 5, 3)


>>> np.rollaxis(a,4,5).shape # the 5 is already at the end.

(1, 2, 3, 4, 5)

NumPy v1.11 和更新版本包含一个新函数 moveaxis,我建议使用它来代替 rollaxis(免责声明:我写的!)。源轴总是在目的地结束,没有任何有趣的差一问题取决于 start 是大于还是小于 end:

import numpy as np

x = np.zeros((1, 2, 3, 4, 5))
for i in range(5):
    print(np.moveaxis(x, 3, i).shape)

结果:

(4, 1, 2, 3, 5)
(1, 4, 2, 3, 5)
(1, 2, 4, 3, 5)
(1, 2, 3, 4, 5)
(1, 2, 3, 5, 4)