NumPy的transpose()方法如何置换数组的轴?

Fra*_* Hu 56 python arrays transpose numpy

In [28]: arr = np.arange(16).reshape((2, 2, 4))

In [29]: arr
Out[29]: 
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7]],

       [[ 8,  9, 10, 11],
        [12, 13, 14, 15]]])


In [32]: arr.transpose((1, 0, 2))
Out[32]: 
array([[[ 0,  1,  2,  3],
        [ 8,  9, 10, 11]],

       [[ 4,  5,  6,  7],
        [12, 13, 14, 15]]])
Run Code Online (Sandbox Code Playgroud)

当我们将一个整数元组传递给transpose()函数时,会发生什么?

具体来说,这是一个3D数组:当我传递轴的元组时,NumPy如何转换数组(1, 0 ,2)?你能解释一下这些整数所指的行或列吗?在NumPy的背景下,轴数是多少?

Ale*_*ley 125

为了转置数组,NumPy只需交换每个轴的形状和步幅信息.以下是大步:

>>> arr.strides
(64, 32, 8)

>>> arr.transpose(1, 0, 2).strides
(32, 64, 8)
Run Code Online (Sandbox Code Playgroud)

请注意,转置操作交换了轴0和轴1的步幅.这些轴的长度也被交换(两个长度都2在此示例中).

没有数据需要复制才能发生这种情况; NumPy可以简单地改变它查看底层内存的方式来构造新数组.


可视化步伐

步幅值表示为了达到数组轴的下一个值而必须在内存中传输的字节数.

现在,我们的3D阵列arr看起来如此(带标记的轴):

在此输入图像描述

该数组存储在连续的内存块中 ; 基本上它是一维的.要将其解释为3D对象,NumPy必须跳过一定的常数字节才能沿三个轴中的一个移动:

在此输入图像描述

由于每个整数占用8个字节的内存(我们使用的是int64 dtype),因此每个维度的步幅值是我们需要跳转的值的8倍.例如,为了沿轴1移动,跳过四个值(32个字节),并且要沿轴0移动,需要跳过八个值(64个字节).

当我们编写时,arr.transpose(1, 0, 2)我们正在交换轴0和1.转置的数组如下所示:

在此输入图像描述

NumPy需要做的就是交换轴0和轴1的步幅信息(轴2不变).现在我们必须进一步跳过轴1而不是轴0:

在此输入图像描述

这个基本概念适用于阵列轴的任何排列.处理转置的实际代码是用C语言编写的,可以在这里找到.

  • 这个答案是非凡的!非常感谢您努力想象Matrix! (9认同)
  • @Alex:谢谢!这些数字是使用[draw.io](https://www.draw.io/)制作的. (3认同)

Rob*_*t B 7

在 C 符号中,您的数组将是:

int arr[2][2][4]
Run Code Online (Sandbox Code Playgroud)

这是一个具有 2 个 2D 阵列的 3D 阵列。每个二维数组都有 2 个一维数组,每个一维数组都有 4 个元素。

所以你有三个维度。轴为 0、1、2,大小为 2、2、4。这正是 numpy 处理 N 维数组轴的方式。

因此,arr.transpose((1, 0, 2))将轴 1 放在位置 0、轴 0 并将其放在位置 1、轴 2 并将其放在位置 2。您实际上是在排列轴:

0 -\/-> 0
1 -/\-> 1
2 ----> 2
Run Code Online (Sandbox Code Playgroud)

换句话说,1 -> 0, 0 -> 1, 2 -> 2。目标轴始终按顺序排列,因此您只需指定源轴即可。按以下顺序读取元组:(1, 0, 2).

在这种情况下,您的新数组维度再次为[2][2][4],仅因为轴 0 和 1 具有相同的大小 (2)。

更有趣的是转置,(2, 1, 0)它为您提供了一个[4][2][2].

0 -\ /--> 0
1 --X---> 1
2 -/ \--> 2
Run Code Online (Sandbox Code Playgroud)

换句话说,2 -> 0, 1 -> 1, 0 -> 2。按以下顺序读取元组:(2, 1, 0).

>>> arr.transpose((2,1,0))
array([[[ 0,  8],
        [ 4, 12]],

       [[ 1,  9],
        [ 5, 13]],

       [[ 2, 10],
        [ 6, 14]],

       [[ 3, 11],
        [ 7, 15]]])
Run Code Online (Sandbox Code Playgroud)

你最终得到了一个int[4][2][2].

如果所有维度的大小不同,您可能会更好地理解,这样您就可以看到每个轴的去向。

为什么是第一个内部元素[0, 8]?因为如果你把你的 3D 阵列想象成两张纸,08排成一行,一张在一张纸上,一张在另一张纸上,都在左上角。通过转置,(2, 1, 0)您是说您希望纸到纸的方向现在从左到右沿着纸行进,而从左到右的方向现在从纸到纸。你有 4 个元素从左到右,所以现在你有四张纸。你有 2 篇论文,所以现在你有 2 个元素从左到右。

抱歉糟糕的 ASCII 艺术。 ¯\_(?)_/¯


Fal*_*lko 6

文档中所述:

默认情况下,反转尺寸,否则根据给定的值置换轴.

因此,您可以传递一个可选参数来axes定义新的维度顺序.

例如,转换RGB VGA像素阵列的前两个维度:

 >>> x = np.ones((480, 640, 3))
 >>> np.transpose(x, (1, 0, 2)).shape
 (640, 480, 3)
Run Code Online (Sandbox Code Playgroud)

  • @FrankHu 在 3d 空间中可视化,所以 x、y 轴旋转就是这里发生的事情。(1,0,2) 从 (0,1,2) 转置,因此,第一个 2 轴被切换。0 是轴的索引号。说,x,y,z 映射到 0,1,2 (2认同)