HOWTO · NumPy

在 NumPy 中转置一维数组

了解如何在 Python 中转置一维 NumPy 数组

本页内容

在 NumPy 中转置一维数组是一个常见的困惑点:对一维 ndarray 调用 .T 可以正常运行、不会报错,但返回的数组看起来却和原来完全一样。要得到真正的转置,需要先把这个一维数组提升为二维行向量或列向量,再转置它。本文解释为什么 .T 不会改变一维数组,并演示添加第二个轴的标准方法。

为什么 .T 不会改变一维数组

一维 ndarray 的形状是 (n,):只有一个轴,其中包含 n 个元素。转置会交换数组各轴的顺序;只有一个轴时就没有可交换的轴,因此 .T 返回同一份数据和相同的形状。检查转置前后的形状:

本节中的代码以及显示的标签或输出仍为英文。

import numpy as np

a = np.array([1, 2, 3, 4, 5])
print(a.shape)
print(a.T.shape)
print(a.T)

输出:

(5,)
(5,)
[1 2 3 4 5]

.T 之后,形状 (5,) 和数组的值都完全相同,这就是人们常说无法转置一维数组的原因。更准确地说,转置确实执行成功了,但它是一次空操作:NumPy 返回的是同一份一维数据的视图。

请记住,.T 是 ndarray 的属性,而不是通用的 Python 特性。普通的 list 没有 .T,因此在执行上述任何操作之前,请先用 np.array() 或 np.asarray() 将列表数据转换为数组。

创建行向量或列向量

实际可行的解决办法是给数组添加第二个轴。行向量的形状是 (1, n),列向量的形状是 (n, 1),两者都会按预期转置。reshape 方法可以这样生成行向量:

本节中的代码以及显示的标签或输出仍为英文。

import numpy as np

a = np.array([1, 2, 3, 4, 5])
row = a.reshape(1, -1)
print(row.shape)
print(row)

输出:

(1, 5)
[[1 2 3 4 5]]

1 固定行数;-1 告诉 NumPy 自动计算列数,使全部五个元素都能放下。使用 a[np.newaxis, :] 可以得到同样的行向量,它会在索引的同时在最前面插入一个新轴。

要得到列向量,只需交换参数:

import numpy as np

a = np.array([1, 2, 3, 4, 5])
col = a.reshape(-1, 1)
print(col.shape)
print(col)

输出:

(5, 1)
[[1]
 [2]
 [3]
 [4]
 [5]]

与之等价的 np.newaxis 写法是 a[:, np.newaxis],它保持原有轴的位置,并在末尾追加一个长度为 1 的新轴。注意打印输出中的双层方括号:它表示这是二维数组,与一维数组的单层方括号相对。

比较 reshape 和 np.newaxis

这两种工具都会在不复制数据的情况下添加一个轴,所以选择哪一种主要关乎可读性而非性能。以下两组写法是等价的:

  • a.reshape(1, -1) 与 a[np.newaxis, :] 生成相同的行向量。
  • a.reshape(-1, 1) 与 a[:, np.newaxis] 生成相同的列向量。

当目标形状是调用的重点时使用 reshape:它明确写出你想要的维度,并且可以一步重排多个轴。当你已经在进行切片操作,或者希望新轴插入的位置在代码中清晰可见时,就在索引中使用 np.newaxis。由于 np.newaxis 是 None 的别名,写成 a[None, :] 的效果完全相同。你可以验证这两组写法产生的结果完全一致:

本节中的代码以及显示的标签或输出仍为英文。

import numpy as np

a = np.array([1, 2, 3, 4, 5])
print(np.array_equal(a.reshape(1, -1), a[np.newaxis, :]))
print(np.array_equal(a.reshape(-1, 1), a[:, np.newaxis]))

输出:

True
True

验证形状和输出

数组变成二维后,.T 就能实现大多数读者真正想要的效果:把行向量变成列向量,把列向量变回行向量。运行下面这段完整检查,并将报告的形状与打印出的值进行对照:

本节中的代码以及显示的标签或输出仍为英文。

import numpy as np

a = np.array([1, 2, 3, 4, 5])
row = a[np.newaxis, :]
col = row.T

print("a:", a.shape)
print("row:", row.shape)
print("row.T:", col.shape)
print(col)

输出:

a: (5,)
row: (1, 5)
row.T: (5, 1)
[[1]
 [2]
 [3]
 [4]
 [5]]

数值保持不变,只有轴的顺序不同。示例代码和输出中的 a:、row:、row.T: 标签按原样保留为英文,分别标识原始数组、行向量以及行向量转置得到的列向量。每当你不确定手中是哪种对象时,就使用 .shape(或 .ndim)查看:打印出 (5,) 说明数组仍是一维的,.T 会原样返回它;而 (1, 5) 或 (5, 1) 则说明它是一个可以转置的二维向量。这一区别对矩阵乘法、模型输入准备等后续工作很重要,因为这些操作要求输入是二维的。