关于python:NumPy的transpose()方法如何排列数组的轴?

How does NumPy's transpose() method permute the axes of an array?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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]]])

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

具体来说,这是一个3D数组:当我传递轴(1, 0 ,2)的元组时,NumPy如何变换该数组? 您能解释这些整数指的是哪行或哪一列? 在NumPy的上下文中,轴号是什么?


为了转置数组,NumPy只需交换每个轴的形状和步幅信息。这是大步前进:

1
2
3
4
5
>>> arr.strides
(64, 32, 8)

>>> arr.transpose(1, 0, 2).strides
(32, 64, 8)

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

无需复制任何数据即可完成此操作; NumPy可以简单地更改其在底层内存中的外观,以构造新的数组。

可视化步幅

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

现在,我们的3D数组arr看起来如下(带有标记的轴):

enter image description here

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

enter image description here

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

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

enter image description here

NumPy所需要做的就是交换轴0和轴1的步幅信息(轴2不变)。现在我们必须跳得更远,才能沿着轴1而不是轴0:

enter image description here

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


如文档中所述:

By default, reverse the dimensions, otherwise permute the axes according to the values given.

因此,您可以传递一个可选参数axes来定义新的尺寸顺序。

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

1
2
3
 >>> x = np.ones((480, 640, 3))
 >>> np.transpose(x, (1, 0, 2)).shape
 (640, 480, 3)


用C表示法,您的数组将是:

1
int arr[2][2][4]

这是具有2个2D阵列的3D阵列。这些2D数组中的每个都有2个1D数组,这些1D数组中的每个都有4个元素。

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

因此,arr.transpose((1, 0, 2))将采用轴1并将其置于位置0,将轴0使其置于位置1,以及将轴2使其置于位置2。您实际上是在对轴进行置换:

1
2
3
0 -\\/-> 0
1 -/\\-> 1
2 ----> 2

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

在这种情况下,仅因为轴0和1具有相同的大小(2),您的新数组尺寸仍然是[2][2][4]

更有趣的是(2, 1, 0)的转置,它为您提供了[4][2][2]的数组。

1
2
3
0 -\\ /--> 0
1 --X---> 1
2 -/ \\--> 2

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

1
2
3
4
5
6
7
8
9
10
11
12
>>> arr.transpose((2,1,0))
array([[[ 0,  8],
        [ 4, 12]],

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

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

       [[ 3, 11],
        [ 7, 15]]])

您以int[4][2][2]结尾。

如果所有尺寸的尺寸都不同,您可能会更好地理解,因此您可以看到每个轴的去向。

为什么第一个内部元素[0, 8]?因为如果将3D阵列可视化为两张纸,则08排列在一张纸上,一张在另一张纸上,都在左上方。通过转置(2, 1, 0),您要说的是现在要使纸到纸的方向从左到右沿着纸行进,而要从左到右的方向现在从纸到纸前进。您从左到右有4个元素,所以现在有了4张纸。而且您有2篇论文,所以现在您有2个元素从左到右。

对不起,可怕的ASCII艺术。 ˉ\\_(ツ)_/ˉ


看来问题和示例出自Wes McKinney的《 Python for Data Analysis》一书。 transpose的此功能在第4.1章中提到。转置数组和交换轴。

For higher dimensional arrays, transpose will accept a tuple of axis numbers to permute the axes (for extra mind bending).

这里的"置换"是指"重新排列",因此要重新排列轴的顺序。

.transpose(1, 0, 2)中的数字确定与原始轴相比如何更改轴的顺序。通过使用.transpose(1, 0, 2),我们的意思是"将第一把斧头改为第二把斧头"。如果使用.transpose(0, 1, 2),则数组将保持不变,因为没有任何更改。这是默认顺序。

书中具有(2, 2, 4)大小的数组的示例不是很清楚,因为第一轴和第二轴的大小相同。因此,除了对arr[0, 1]arr[1, 0]行进行重新排序之外,最终结果似乎没有改变。

如果我们尝试使用3维数组(每个维具有不同大小)的不同示例,则重排部分将变得更加清晰。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
In [2]: x = np.arange(24).reshape(2, 3, 4)

In [3]: x
Out[3]:
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11]],

       [[12, 13, 14, 15],
        [16, 17, 18, 19],
        [20, 21, 22, 23]]])

In [4]: x.transpose(1, 0, 2)
Out[4]:
array([[[ 0,  1,  2,  3],
        [12, 13, 14, 15]],

       [[ 4,  5,  6,  7],
        [16, 17, 18, 19]],

       [[ 8,  9, 10, 11],
        [20, 21, 22, 23]]])

在此,原始数组大小为(2, 3, 4)。我们更改了1st和2nd,因此它的大小变为(3, 2, 4)。如果我们仔细观察,看看重新排列是如何发生的;数字数组似乎已在特定模式下发生了变化。使用@RobertB的书面类比,如果我们要取2个数字块,并将每个数字写在纸上,然后从每个纸上取一行,以构造数组的一个维,那么我们现在将得到一个3x2x4大小的数组,从最外层到最内层。

1
[ 0,  1,  2,  3] \\ [12, 13, 14, 15]
1
[ 4,  5,  6,  7] \\ [16, 17, 18, 19]
1
[ 8,  9, 10, 11] \\ [20, 21, 22, 23]

最好使用不同大小的数组,并更改不同的轴以更好地了解其工作原理。


总结a.transpose()[i,j,k] = a [k,j,i]

1
2
3
a = np.array( range(24), int).reshape((2,3,4))
a.shape gives (2,3,4)
a.transpose().shape gives (4,3,2)  shape tuple is reversed.

当是元组参数时,将根据元组对轴进行排列。
例如

一个= np.array(range(24),int).reshape((2,3,4))

a [i,j,k]等于a.transpose((2,0,1))[k,i,j]

轴0排名第二

轴1排名第三

轴2故事第一名

当然,我们需要注意传递给转置的元组参数中的值是唯一的,并且在范围(轴数)中