NumPy Transpose 介绍与用法详解 – wiki基地


NumPy Transpose:介绍、原理与详细用法解析

在科学计算和数据分析领域,NumPy 库是 Python 中不可或缺的工具。它提供了强大的多维数组对象(ndarray),以及用于处理这些数组的各种高效函数。在这些操作中,数组的转置(Transpose)是一个非常基础且重要的概念,尤其是在线性代数、数据处理和机器学习算法的实现中。

本文将带您深入了解 NumPy 中的数组转置,包括其数学原理、NumPy 提供的多种实现方式(.T 属性、np.transpose() 函数、.swapaxes() 方法),以及它们各自的用法、区别和适用场景。

第一部分:什么是转置?数学与概念

在数学中,矩阵的转置是一个基本操作。对于一个 m 行 n 列的矩阵 A,其转置矩阵 AT 是一个 n 行 m 列的矩阵,它的第 i 行第 j 列元素是 A 的第 j 行第 i 列元素。简单来说,就是将原矩阵的行变成列,列变成行。

例如,一个 2×3 的矩阵 A:

A = [[1, 2, 3],
[4, 5, 6]]

它的转置 AT 是一个 3×2 的矩阵:

A^T = [[1, 4],
[2, 5],
[3, 6]]

这种概念可以推广到更高维度的数组。对于一个 N 维数组,转置不再仅仅是“行变列”,而是涉及到轴(axes)的重新排列。一个 N 维数组有 N 个轴,通常从 0 到 N-1 进行索引。转置操作就是按照指定的顺序重新排列这些轴。默认的转置操作通常是指将轴的顺序反转。

例如,一个 3 维数组,形状为 (d0, d1, d2),默认转置(或使用 .T)后,其形状会变成 (d2, d1, d0)。更通用的转置允许你指定一个轴的排列顺序,例如将形状 (d0, d1, d2) 转置成 (d1, d2, d0),这意味着原来的第 1 轴变成了新数组的第 0 轴,原来的第 2 轴变成了新数组的第 1 轴,原来的第 0 轴变成了新数组的第 2 轴。

第二部分:NumPy 中的转置方法

NumPy 提供了多种方法来执行数组的转置操作,主要包括:

  1. .T 属性
  2. np.transpose() 函数
  3. .swapaxes() 方法

它们在功能上有所重叠,但在灵活性和使用习惯上有所差异。

2.1 使用 .T 属性

.T 是 NumPy 数组对象的一个属性,它是执行转置操作最简洁、最常用的方式,尤其适用于二维数组。

  • 用法: array.T
  • 返回值: 返回一个转置后的数组的视图(View)。视图意味着转置后的数组与原数组共享底层数据,修改其中一个会影响另一个。
  • 特点: 简洁易用,适用于任何维度的数组,但对于二维数组之外,其行为是默认的轴顺序反转,不够灵活。

示例 1:二维数组的转置

“`python
import numpy as np

创建一个二维 NumPy 数组

arr_2d = np.array([[1, 2, 3],
[4, 5, 6]])

print(“原二维数组 arr_2d:”)
print(arr_2d)
print(“形状:”, arr_2d.shape) # (2, 3)

使用 .T 进行转置

arr_2d_T = arr_2d.T

print(“\n转置后的二维数组 arr_2d_T:”)
print(arr_2d_T)
print(“形状:”, arr_2d_T.shape) # (3, 2)

验证视图特性:修改转置后的数组

arr_2d_T[0, 1] = 99

print(“\n修改 arr_2d_T 后,原数组 arr_2d:”)
print(arr_2d) # 原数组也发生了变化
“`

在这个例子中,一个 2×3 的数组通过 .T 变成了 3×2 的数组,行和列互换。修改转置后的数组 arr_2d_T 的元素,原数组 arr_2d 也随之改变,证明 .T 返回的是一个视图。

示例 2:一维数组的转置

一维数组在概念上可以被视为行向量或列向量。NumPy 中的一维数组只有一个轴(轴 0),其形状是 (n,)。对一维数组使用 .T 操作,其形状和元素顺序都不会改变。

“`python

创建一个一维 NumPy 数组

arr_1d = np.array([1, 2, 3, 4])

print(“\n原一维数组 arr_1d:”)
print(arr_1d)
print(“形状:”, arr_1d.shape) # (4,)

使用 .T 进行转置

arr_1d_T = arr_1d.T

print(“\n转置后的一维数组 arr_1d_T:”)
print(arr_1d_T)
print(“形状:”, arr_1d_T.shape) # (4,)
print(“arr_1d is arr_1d_T:”, arr_1d is arr_1d_T) # 通常是 True
“`

结果显示,一维数组转置后形状和内容不变。这符合数学中将行向量转置为列向量(或反之)需要形状从 (1, n) 变为 (n, 1)(或反之)的概念。NumPy 的一维数组本身不区分行或列,因此 .T 操作对其没有影响。如果需要将一维数组明确转换为行向量或列向量,通常需要使用 reshape 或增加新的维度(例如 arr[:, np.newaxis]arr[np.newaxis, :])。

示例 3:高维数组的转置

对于三维或更高维度的数组,.T 属性执行的是默认转置:反转轴的顺序。例如,对于一个形状为 (d0, d1, d2) 的三维数组,.T 会将其转置为形状 (d2, d1, d0) 的数组。

“`python

创建一个三维 NumPy 数组

形状 (2, 3, 4)

arr_3d = np.arange(2 * 3 * 4).reshape(2, 3, 4)

print(“\n原三维数组 arr_3d:”)
print(arr_3d)
print(“形状:”, arr_3d.shape) # (2, 3, 4)

使用 .T 进行转置

arr_3d_T = arr_3d.T

print(“\n转置后的三维数组 arr_3d_T (使用 .T):”)
print(arr_3d_T)
print(“形状:”, arr_3d_T.shape) # (4, 3, 2)
“`

可以看到,原形状 (2, 3, 4) 的数组转置后变成了 (4, 3, 2),轴的顺序 (0, 1, 2) 被反转为 (2, 1, 0)

2.2 使用 np.transpose() 函数

np.transpose() 是 NumPy 中更通用、更灵活的转置函数。它允许你指定任意的轴顺序来重新排列数组的维度。

  • 用法: np.transpose(a, axes=None)
    • a: 需要转置的数组。
    • axes: 一个表示新轴顺序的元组或列表。它的长度必须与数组 a 的维度相同。例如,对于一个 N 维数组,axes 应该是一个包含从 0 到 N-1 的整数的排列,指定了原数组的哪个轴将成为新数组的哪个轴。如果 axesNone(默认值),则反转轴的顺序,这与 .T 的行为一致。
  • 返回值: 返回一个转置后的数组的视图。
  • 特点: 提供了对轴顺序的完全控制,可以实现任意的维度排列。

示例 4:二维数组的转置 (使用 np.transpose)

对于二维数组,np.transpose(arr, axes=(1, 0)) 是标准的转置操作,等同于 arr.T

“`python

使用之前的二维数组 arr_2d

print(“\n原二维数组 arr_2d:”)
print(arr_2d)
print(“形状:”, arr_2d.shape) # (2, 3)

使用 np.transpose 进行转置,指定 axes=(1, 0)

arr_2d_T_func = np.transpose(arr_2d, axes=(1, 0))

print(“\n转置后的二维数组 arr_2d_T_func (使用 np.transpose, axes=(1, 0)):”)
print(arr_2d_T_func)
print(“形状:”, arr_2d_T_func.shape) # (3, 2)

使用 np.transpose 进行默认转置 (axes=None)

arr_2d_T_default = np.transpose(arr_2d, axes=None) # 或 np.transpose(arr_2d)

print(“\n转置后的二维数组 arr_2d_T_default (使用 np.transpose, axes=None):”)
print(arr_2d_T_default)
print(“形状:”, arr_2d_T_default.shape) # (3, 2)

print(“arr_2d_T_func 和 arr_2d_T_default 是否相同:\n”, np.array_equal(arr_2d_T_func, arr_2d_T_default))
“`

结果表明,使用 np.transpose 并指定 axes=(1, 0)axes=None 对于二维数组的效果是相同的,都实现了标准的行/列互换。

示例 5:高维数组的灵活转置 (使用 np.transpose)

np.transpose 的强大之处在于对高维数组轴的灵活控制。

“`python

使用之前的三维数组 arr_3d,形状 (2, 3, 4)

print(“\n原三维数组 arr_3d:”)
print(arr_3d)
print(“形状:”, arr_3d.shape) # (2, 3, 4)

将原数组的轴顺序 (0, 1, 2) 重新排列为 (1, 0, 2)

原来的轴 1 (大小为 3) 变成新数组的轴 0

原来的轴 0 (大小为 2) 变成新数组的轴 1

原来的轴 2 (大小为 4) 变成新数组的轴 2

arr_3d_T_axes = np.transpose(arr_3d, axes=(1, 0, 2))

print(“\n转置后的三维数组 arr_3d_T_axes (使用 np.transpose, axes=(1, 0, 2)):”)

注意观察输出,原本在外层的维度现在变成了第二层

原本在第二层的维度现在变成了最外层

print(arr_3d_T_axes)
print(“形状:”, arr_3d_T_axes.shape) # (3, 2, 4)

将原数组的轴顺序 (0, 1, 2) 重新排列为 (2, 0, 1)

原来的轴 2 (大小为 4) 变成新数组的轴 0

原来的轴 0 (大小为 2) 变成新数组的轴 1

原来的轴 1 (大小为 3) 变成新数组的轴 2

arr_3d_T_axes2 = np.transpose(arr_3d, axes=(2, 0, 1))

print(“\n转置后的三维数组 arr_3d_T_axes2 (使用 np.transpose, axes=(2, 0, 1)):”)
print(arr_3d_T_axes2)
print(“形状:”, arr_3d_T_axes2.shape) # (4, 2, 3)
“`

通过指定 axes 参数,我们可以任意组合原数组的轴来创建新的数组视图。这是 np.transpose 最强大的地方。

2.3 使用 .swapaxes() 方法

.swapaxes(axis1, axis2) 是 NumPy 数组对象的一个方法,用于交换数组中的两个指定的轴。

  • 用法: array.swapaxes(axis1, axis2)
    • axis1: 第一个要交换的轴的索引。
    • axis2: 第二个要交换的轴的索引。
  • 返回值: 返回一个交换了指定轴顺序的数组的视图。
  • 特点: 专门用于交换 两个 轴,其功能是 np.transpose(arr, axes=...) 的一个特例,即 arr.swapaxes(ax1, ax2) 等价于 np.transpose(arr, axes=...),其中 axes 是原轴顺序经过交换 ax1ax2 得到的排列。在只需要交换两个轴时,.swapaxes() 可能更直观。

示例 6:二维数组交换轴 (使用 .swapaxes)

对于二维数组,只有轴 0 和轴 1,交换它们就相当于转置。

“`python

使用之前的二维数组 arr_2d

print(“\n原二维数组 arr_2d:”)
print(arr_2d)
print(“形状:”, arr_2d.shape) # (2, 3)

使用 .swapaxes 交换轴 0 和轴 1

arr_2d_swap = arr_2d.swapaxes(0, 1)

print(“\n交换轴后的二维数组 arr_2d_swap (使用 .swapaxes(0, 1)):”)
print(arr_2d_swap)
print(“形状:”, arr_2d_swap.shape) # (3, 2)

print(“arr_2d_swap 和 arr_2d.T 是否相同:\n”, np.array_equal(arr_2d_swap, arr_2d.T))
“`

结果表明,对于二维数组,arr.swapaxes(0, 1) 的效果与 arr.Tnp.transpose(arr, axes=(1, 0)) 完全相同。

示例 7:高维数组交换轴 (使用 .swapaxes)

.swapaxes() 在高维数组中用于交换任意两个指定的轴。

“`python

使用之前的三维数组 arr_3d,形状 (2, 3, 4),轴索引 (0, 1, 2)

print(“\n原三维数组 arr_3d:”)
print(arr_3d)
print(“形状:”, arr_3d.shape) # (2, 3, 4)

交换轴 0 和轴 2

原来的轴顺序 (0, 1, 2) 变为 (2, 1, 0)

arr_3d_swap02 = arr_3d.swapaxes(0, 2)

print(“\n交换轴 0 和 2 后的三维数组 arr_3d_swap02 (使用 .swapaxes(0, 2)):”)
print(arr_3d_swap02)
print(“形状:”, arr_3d_swap02.shape) # (4, 3, 2)

注意,这与 arr_3d.T 的结果是相同的,因为 .T 默认就是反转轴顺序,对于 3D 数组即 (2, 1, 0)

交换轴 1 和轴 2

原来的轴顺序 (0, 1, 2) 变为 (0, 2, 1)

arr_3d_swap12 = arr_3d.swapaxes(1, 2)

print(“\n交换轴 1 和 2 后的三维数组 arr_3d_swap12 (使用 .swapaxes(1, 2)):”)
print(arr_3d_swap12)
print(“形状:”, arr_3d_swap12.shape) # (2, 4, 3)
“`

arr_3d.swapaxes(0, 2) 的结果形状是 (4, 3, 2),这恰好是 arr_3d.T 的结果,也等同于 np.transpose(arr_3d, axes=(2, 1, 0))arr_3d.swapaxes(1, 2) 的结果形状是 (2, 4, 3),这等同于 np.transpose(arr_3d, axes=(0, 2, 1))

第三部分:选择哪种方法?区别与建议

  • .T 属性:
    • 优点: 最简洁,代码可读性高,尤其适合二维数组的标准转置。
    • 缺点: 只能执行默认的轴顺序反转,灵活性最低。
    • 适用场景: 二维数组的标准转置,或者高维数组需要默认转置时。
  • np.transpose() 函数:
    • 优点: 功能最强大,可以通过 axes 参数实现任意维度的重新排列。
    • 缺点: 相对于 .T 稍微复杂一些,需要理解轴的概念和排列方式。
    • 适用场景: 需要灵活控制轴顺序的任何情况,尤其是高维数组。
  • .swapaxes() 方法:
    • 优点: 当只需要交换 两个 轴时,其意图可能比 np.transpose 搭配 axes 参数更清晰。
    • 缺点: 只能交换两个轴,不够通用。
    • 适用场景: 只需要交换数组中两个特定轴时。

总结来说:

  • 对于二维数组的标准转置,优先使用 .T,因为它最简洁。
  • 对于需要交换高维数组中的任意两个轴,可以使用 .swapaxes() 以增强代码可读性,或者使用 np.transpose() 搭配相应的 axes 参数
  • 对于需要对高维数组进行复杂的、涉及多个轴的重新排列,必须使用 np.transpose() 并精确指定 axes 参数
  • 在不确定或需要通用性时,np.transpose() 是最稳妥的选择,通过 axes=None 可以实现默认转置,通过指定 axes 可以实现任意排列。

第四部分:转置的应用场景

转置操作在实际的数据处理和数值计算中无处不在:

  1. 矩阵乘法: 在进行矩阵乘法 A @ B 时,要求矩阵 A 的列数等于矩阵 B 的行数。如果数据以某种方式存储,而形状不满足要求,通常需要对其中一个矩阵进行转置。例如,如果想计算矩阵 A 和矩阵 B 的转置的乘积 A @ B.T
  2. 数据预处理: 在机器学习中,数据通常以表格形式存储,行代表样本,列代表特征。但在某些算法或库中,可能要求输入数据的格式是特征作为行,样本作为列,这时就需要对数据矩阵进行转置。
  3. 线性代数运算: 求解线性方程组、计算特征值/特征向量、奇异值分解等许多线性代数操作都涉及到矩阵的转置。
  4. 信号处理和图像处理: 在处理多维信号或图像数据时,经常需要调整维度顺序以适应特定的算法或库函数,转置(或更广义的轴排列)是实现这一目的的关键。
  5. 广播机制: 虽然不是直接的转置应用,但理解转置(以及轴的概念)有助于更好地理解 NumPy 的广播(broadcasting)规则,有时通过转置可以使原本不兼容形状的数组变得兼容。

第五部分:视图与复制

在 NumPy 中,转置操作(通过 .T, np.transpose(), .swapaxes())通常返回原数组的一个视图 (View),而不是一个全新的数组副本。这意味着转置后的数组并没有占用额外的内存来存储完整的数据副本,它只是提供了一种不同的方式来解释和访问原数组的数据。因此,修改转置后的数组元素会直接影响到原数组,反之亦然。

“`python

再次验证视图特性

arr = np.array([[1, 2], [3, 4]])
arr_t = arr.T

print(“原数组:”, arr)
print(“转置数组:”, arr_t)

修改转置数组的元素

arr_t[0, 1] = 100

print(“\n修改转置数组后:”)
print(“原数组:”, arr)
print(“转置数组:”, arr_t)
“`

可以看到,对 arr_t 的修改影响了 arr。这是因为它们指向同一块内存数据。这种特性使得转置操作非常高效,因为它避免了大量数据的复制。

然而,需要注意的是,视图的连续性(contiguous)可能与原数组不同。NumPy 数组的数据在内存中通常是连续存储的(C-contiguous 或 Fortran-contiguous),这有助于提高某些操作的效率。转置操作可能会改变数据的逻辑访问顺序,使其在内存中变得不再是C-contiguous或Fortran-contiguous。你可以通过 .flags 属性查看数组的内存布局信息。如果需要一个具有特定连续性的转置数组副本,可以使用 .copy() 方法。

“`python
arr = np.arange(6).reshape(2, 3)
arr_t = arr.T # 这是一个视图

print(“\n原数组 flags:”, arr.flags)
print(“转置数组 flags:”, arr_t.flags) # C_CONTIGUOUS可能是False

如果需要一个独立的、C-contiguous 的转置副本

arr_t_copy = arr.T.copy()

print(“\n转置副本 flags:”, arr_t_copy.flags) # C_CONTIGUOUS通常是True
print(“arr_t_copy 是否是 arr_t 的视图:”, arr_t_copy is arr_t)
print(“arr_t_copy 是否与 arr_t 数据相同:\n”, np.array_equal(arr_t_copy, arr_t))

修改副本不会影响原数组

arr_t_copy[0, 0] = 999
print(“\n修改转置副本后,原数组:”, arr)
“`

输出会显示 arr_tC_CONTIGUOUS flag 可能为 False(对于这个例子,它可能是 Fortran-contiguous),而 arr_t_copyC_CONTIGUOUS flag 为 True。修改 arr_t_copy 并不会影响原数组 arr

结论

NumPy 中的转置操作是处理多维数组和进行数值计算的基石之一。通过 .T 属性、np.transpose() 函数和 .swapaxes() 方法,NumPy 提供了从简单二维转置到复杂高维轴排列的各种功能。理解这些方法的使用、它们之间的区别以及视图/副本的概念,对于高效和正确地使用 NumPy 进行数据处理和算法实现至关重要。在实际编程中,根据具体需求选择最合适、最清晰的转置方式,并注意视图特性可能带来的影响。熟练掌握转置将极大地提升您使用 NumPy 的能力。


发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部