NumPy的transpose()方法如何对数组的轴进行排列?

122
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()函数时,会发生什么?

具体来说,这是一个三维数组:当我传递轴的元组(1, 0, 2)时,NumPy如何转换数组?您能解释一下这些整数引用哪一行或哪一列吗?在NumPy的上下文中,轴编号是什么意思?


http://docs.scipy.org/doc/numpy/reference/generated/numpy.transpose.html - Padraic Cunningham
3
0 是第一轴,1 是第二轴,2 是第三轴,等等...... transpose 函数的 axes 参数指定了你想要重新排列的新顺序,例如:先放置第二轴,然后是第一轴,最后是第三轴。 - Jaime
1
在3D显示中,有块、行和列。您的转置已经交换了块和行的顺序,但列保持不变。原来第1个块的第2行现在成为第2个块的第1行。 - hpaulj
6个回答

280
为了对数组进行转置,NumPy只需交换每个轴的形状和步幅信息。以下是步幅信息:
>>> arr.strides
(64, 32, 8)

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

请注意,转置操作交换了轴0和轴1的步幅。这些轴的长度也被交换(在此示例中,这两个长度都为2)。
不需要复制任何数据即可实现此操作;NumPy只需更改其查看底层内存的方式即可构造新数组。

可视化步幅

步幅值表示在内存中到达数组轴的下一个值所必须走过的字节数。

现在,我们的三维数组arr如下所示(带有标记的轴):

enter image description here

这个数组被存储在一个连续的内存块中,本质上是一维的。为了将其解释为一个三维对象,NumPy必须跳过一定数量的常量字节以沿着其中的某一个轴移动:

enter image description here

由于每个整数占用8字节的内存(我们使用int64 dtype),因此每个维度的跨度值是需要跳过的值的数量乘以8。例如,沿轴1移动时,要跳过4个值(32字节),沿轴0移动时,需要跳过8个值(64字节)。
当我们写arr.transpose(1, 0, 2)时,我们交换轴0和轴1。转置后的数组如下所示:

enter image description here

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

enter image description here

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

12

正如文档中所解释的:

默认情况下,翻转维度,否则根据给定的值重新排列轴。

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

例如,转置RGB VGA像素数组的前两个维度:

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

1
你好,感谢您的回答。然而,我仍然无法理解这个问题,正如您所看到的,arr的形状是(2,2,4),而arr.transpose(1,0,2)也是(2,2,4)。具体来说,这是一个三维数组,当我传递(1,0,2)时,如何转换数组?您能解释一下1、0、2指的是哪一行或哪一列吗?在numpy中,轴编号是什么意思? - Frank Hu
2
@FrankHu 在三维空间中进行可视化,因此发生了x、y轴旋转。 (1,0,2) 从 (0,1,2) 转置而来,因此前两个轴被交换。0 是轴的索引号。比如,x、y、z 分别映射为 0、1、2。 - CodeFarmer

6

在C语言中,数组应该是这样的:

int arr[2][2][4]

这是一个3D数组,其中包含2个2D数组。每个2D数组都有2个1D数组,每个1D数组都有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

换句话说,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

换句话说,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]]])

你现在得到了一个 int[4][2][2] 数组。

如果每个维度的大小都不同,你可能会更好地理解,从而看到每个轴的方向。

为什么第一个内部元素是 [0, 8]?因为如果你将你的三维数组视为两张纸,08 在一起,一张在另一张上,两者都位于左上角。通过转置 (2, 1, 0),你正在说你希望纸对纸的方向现在沿着纸从左到右移动,从左到右的方向现在从纸到纸移动。你有4个元素从左到右,所以现在你有四张纸。你有2张纸,所以现在你有2个元素从左到右。

很抱歉我的ASCII艺术很糟糕。¯\_(ツ)_/¯


我想问一下,您提供给 transpose() 的元组不是数学排列或其他什么吧?它只是字面上的指令,表示“现在将轴放在这些位置”吗?例如,对于 .transpose(p, q, r, s),您是在说“将轴 p 放在第0个位置,将轴 q 放在第1个位置,将轴 r 放在第2个位置,将轴 s 放在第3个位置”?或者换句话说,b = a.transpose(axes) 意味着 b.shape == tuple(a.shape[i] for i in axes) - Tim
通过用 ASCII 艺术交换轴的想法,我对此有了更清晰的理解。非常感谢。 - K_inverse

4
看起来问题和示例来自Wes McKinney的书《Python数据分析》。在第4.1章“转置数组和交换轴”中提到了transpose的这个特性。
对于更高维的数组,transpose将接受一个轴数元组以重新排列轴(非常令人费解)。这里的“重新排列”意味着重新排列轴的顺序。 .transpose(1,0,2)中的数字决定了与原始顺序相比如何改变轴的顺序。使用.transpose(1,0,2)的意思是“用第二个轴替换第一个轴”。如果我们使用.transpose(0,1,2),数组将保持不变,因为没有需要改变的内容;这是默认顺序。
书中用(2,2,4)大小的数组举的例子不太清楚,因为第一和第二轴具有相同的大小。因此最终结果似乎除了行arr[0,1]arr[1,0]的重新排序之外没有什么改变。
如果我们尝试一个不同的例子,其中三维数组的每个维度都具有不同的大小,则重新排列部分变得更加清晰。
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)。我们改变了第一和第二个,因此它的大小变成了(3, 2, 4)。如果我们仔细观察重新排列发生的确切方式;数字数组似乎以一种特定的模式发生了变化。使用@RobertB的纸张类比,如果我们将两个数字块写在纸上,然后从每个纸张中取出一行来构造一个数组维度,我们现在将有一个大小为3x2x4的数组,从最外层到最内层进行计数。
[ 0,  1,  2,  3] \ [12, 13, 14, 15]

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

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

尝试使用不同大小的数组,并更改不同的轴以更好地理解其工作可能是一个好主意。


我使用以下命令从.tif图像中获取通道:(red, green, blue, alpha) = np.transpose(img, axes=(2,0,1)) 如果我想读取单通道的.tif文件,例如:gray = np.transpose(????),那么我应该对np.transpose进行哪些修改? - just_learning

3

我在 Wes McKinney 的《Python数据分析》中也看到了这个。

我将展示解决 3 维张量的最简单方法,然后描述可用于 n 维张量的一般方法。

简单的 3 维张量示例

假设您有一个(2,2,4)张量:

[[[ 0  1  2  3]
  [ 4  5  6  7]]

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

如果我们查看每个点的坐标,它们如下:

[[[ (0,0,0)  (0,0,1)  (0,0,2)  (0,0,3)]
  [ (0,1,0)  (0,1,1)  (0,1,2)  (0,1,3)]]

 [[ (1,0,0)  (1,0,1) (1,0,2) (0,0,3)]
  [ (1,1,0)  (1,1,1) (1,1,2) (0,1,3)]]

假设上面的数组是example_array,我们想执行操作:example_array.transpose(1,2,0)

对于(1,2,0)转换,我们按以下方式重新排列坐标(请注意,这个特定的转换相当于“向左移位”):

(0,0,0)  ->  (0,0,0)
(0,0,1)  ->  (0,1,0)
(0,0,2)  ->  (0,2,0)
(0,0,3)  ->  (0,3,0)
(0,1,0)  ->  (1,0,0)
(0,1,1)  ->  (1,1,0)
(0,1,2)  ->  (1,2,0)
(0,1,3)  ->  (1,3,0)
(1,0,0)  ->  (0,0,1)
(1,0,1)  ->  (0,1,1)
(1,0,2)  ->  (0,2,1)
(0,0,3)  ->  (0,3,0)
(1,1,0)  ->  (1,0,1)
(1,1,1)  ->  (1,1,1)
(1,1,2)  ->  (1,2,1)
(0,1,3)  ->  (1,3,0)

现在,对于每个原始值,在结果矩阵中放置它到偏移后的坐标。

例如,值 10 在原始矩阵中的坐标为 (1, 0, 2),在结果矩阵中将具有坐标 (0, 2, 1)。它被放置在第三行中的第一个2d张量子矩阵中,在该子矩阵的第二列行。

因此,得到的矩阵为:

array([[[ 0,  8],
        [ 1,  9],
        [ 2, 10],
        [ 3, 11]],

       [[ 4, 12],
        [ 5, 13],
        [ 6, 14],
        [ 7, 15]]])

通用的n维张量方法

对于n维张量,算法与二维矩阵相同。考虑原始矩阵中单个值的所有坐标。对该单个坐标的轴进行重排。将该值放入结果矩阵中相应的重排坐标中。对所有剩余的值重复此操作。


-2

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

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.

当元组参数传递时,轴会根据元组重新排列。例如

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

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

轴0占第二个位置

轴1占第三个位置

轴2占第一个位置

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


你没有回答楼主的问题,即当你指定轴时进行转置会发生什么。 - Robert B
当指定轴时,添加对问题的响应。 - Raghu Ram

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接