【问题标题】:Reason why numpy rollaxis is so confusing?numpy rollaxis 如此令人困惑的原因是什么?
【发布时间】:2017-07-15 05:49:38
【问题描述】:

numpy rollaxis 函数的行为让我很困惑。 documentation 说:

向后滚动指定轴,直到它位于给定位置。

对于start 参数:

轴滚动直到它位于这个位置之前。

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

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

>>> 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 索引?

【问题讨论】:

  • 我同意,文档(和观察到的行为)不匹配。此外,文档也没有提到可以使用负索引(如 python 列表索引)来倒数。 GitHub 上似乎至少有一个关于此主题的问题。
  • 对应的transpose 输入是否更容易理解? np.transpose(a,[0,2,3,1]).shapenp.transpose(a,[0,3,1,2]).shape
  • 在第二种情况下,a.shape[3]a.shape[1] 之前结束,64 之前结束。在第一种情况下,没有a.shape[4],所以插入在末尾。
  • 我不明白滚动轴是什么意思?它说滚动时是否意味着旋转索引数组?

标签: python numpy scipy


【解决方案1】:

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)

【讨论】:

    【解决方案2】:

    大部分困惑源于我们人类的直觉——我们如何看待移动轴。我们可以指定滚动步数(前后 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-=1 说明remove 操作。

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

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

    我怀疑rollaxis 旨在成为transpose 的更直观版本。能不能实现是另一个问题。


    您建议省略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]。在滚动3 之后是a.shape[1]。但这是一个不同的 roll 规范。

    这取决于start 的定义方式。是原单中的仓位,还是退回单中的仓位?


    如果您更愿意将start 视为最终形状中的索引位置,那么删除before 部分并说“将axis 移动到dest 插槽”不是更简单吗?

    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)
    

    【讨论】:

    • 很好的解释,但是我认为这个实现中的缺陷是将axis < start 案例与axis >= start 案例区分开来。如果这被省略(或start -= 1 两种情况),行为将是一致的。
    • 实际上我将start 视为轴列表中的索引。由于轴的数量没有改变,start 是指原始轴顺序还是结果轴顺序都没有关系。基本上调用 np.rollaxis(a, 3, 1) 表示“我希望将索引 3 处的轴移动到索引 0(在 1 之前)”,就像在 np.rollaxis(a, 1, 3) 示例中将索引 1 处的轴移动到索引 2(在 3 之前)一样。
    • 我添加了一个替代卷,它使用dest 索引而不是“开始前”。
    • 如果还要处理负索引,只需执行axis %= a.ndimdest %= a.ndim
    • 老实说,在我看来-=“更正”是一个错误,一旦开发人员习惯了它就会卡住。我想不出为什么这两个应该给出相同的行为:np.rollaxis(a, 1, 1)np.rollaxis(a, 1, 2)。并且没有优雅的方法可以让任何轴成为最后一个轴(老实说,这是我主要想要的用途)。我应该从np.rollaxis(a, axis, -1) 得到它,但我必须检查ndim 才能做到这一点:np.rollaxis(a, axis, a.ndim) 这似乎很愚蠢,因为rollaxis 已经在内部检查了ndim
    【解决方案3】:
    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)
    

    将轴移动到末端怎么样?嗯,end 后面没有数字,但是你可以指定 start 为 end 之后。

    例子:

    >>> 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)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-09-28
      • 1970-01-01
      • 1970-01-01
      • 2014-12-30
      • 2010-12-17
      • 2022-01-21
      • 2022-01-17
      • 2011-04-13
      相关资源
      最近更新 更多