本系列是《NumPy 速通》的配套章节。

调整数组的"长相"——重塑、展平、转置、增删维度,是数据处理和喂给模型的必经步骤。核心心法:shape 变了,但底层数据(和顺序)没变。

reshape —— 重塑形状

不改变元素个数,只改变排列方式。注意顺序:按行优先(C 顺序)逐行填充:

import numpy as np

arr = np.arange(12)   # [0 1 2 3 4 5 6 7 8 9 10 11]

arr.reshape(3, 4)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]

arr.reshape(4, 3)
# [[ 0  1  2]
#  [ 3  4  5]
#  [ 6  7  8]
#  [ 9 10 11]]

arr.reshape(2, 6)   # 2 行 6 列

-1 让 NumPy 自动算维度

arr.reshape(2, -1)   # 自动算出 6 列 → (2, 6)
arr.reshape(-1, 3)   # 自动算出 4 行 → (4, 3)
arr.reshape(-1)      # 展平成一维(等价的另一种写法)

-1 只能出现一次(否则无法确定)。

⚠️ 元素总数必须一致

np.arange(12).reshape(3, 5)   # ❌ ValueError:12 无法变成 3×5=15

报错 cannot reshape array of size 12 into shape (3,5)。reshape 前先用 arr.size 确认。

flatten 与 ravel —— 展平成一维

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

arr.flatten()   # [1 2 3 4]  返回副本(新内存)
arr.ravel()     # [1 2 3 4]  返回视图(尽量共享内存,省内存)

区别:

  • flatten():总是副本,改它不影响原数组
  • ravel():尽量返回视图(共享内存,快);仅在不得已时才复制
r = arr.ravel()
r[0] = 99
print(arr)   # [[99 2] [3 4]]  ⚠️ 原数组被改了(ravel 是视图)!

⚠️ 想展平后安全修改,用 flatten();只读统计用 ravel()(更快更省内存)。

转置 —— 行列互换

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

arr.T          # [[1 4] [2 5] [3 6]]  行列互换,(2,3)→(3,2)
np.transpose(arr)   # 等价写法
arr.transpose()     # 也是等价写法

一维数组转置没变化([1 2 3].T 还是 [1 2 3])。

⚠️ 灵魂警告:转置是视图(零拷贝、共享内存),不是副本!t = arr.T; t[0,0] = 99 会改原数组。要独立副本用 arr.T.copy()。

另外转置不"搬运数据",只是改了 strides(步长),所以转置是 O(1) 零成本——这也是 NumPy 高效的原因之一。

多维转置 / 轴重排(进阶)

arr3d = np.arange(24).reshape(2, 3, 4)

arr3d.transpose(2, 1, 0)   # 指定轴的排列顺序
arr3d.swapaxes(0, 2)       # 交换两个轴

深度学习里 (batch, height, width, channel) → (batch, channel, height, width) 就靠 transpose。

增加 / 删除维度

newaxis / expand_dims —— 加维度

arr = np.array([1, 2, 3])   # shape (3,)

arr[np.newaxis, :]    # (1, 3)  变成"一行"
arr[:, np.newaxis]    # (3, 1)  变成"一列"
np.expand_dims(arr, 0)  # (1, 3)  等价于第一种
np.expand_dims(arr, 1)  # (3, 1)  等价于第二种

💡 np.newaxis 加长度为 1 的维度,常用于配合广播(第 8 章):(3,) 没法直接和 (5,3) 做"每列"运算,但 (3,1) 可以。这是数据变形最常用的技巧之一。

squeeze —— 删掉长度为 1 的维度

arr = np.array([[[1, 2, 3]]])   # shape (1, 1, 3)

np.squeeze(arr)     # [1 2 3]  删掉所有长度为 1 的维度
np.squeeze(arr, axis=0)  # (1, 3)  只删指定轴
arr.squeeze()       # 方法形式

squeeze 常用来去掉"多余的 1",比如模型输出 (1, 1, 10) 变成 (10,)。

⚠️ squeeze 默认删所有长度为 1 的轴。想精准删哪个用 axis= 指定。删完 shape 变了对不上号,报错前先确认。

组合实战:把一维变"每列"

最常见的需求:(3,) 想按"每一列"去广播,先升维再运算:

col = np.array([10, 20, 30])

col[:, np.newaxis]   # (3, 1)  每列一个
# [[10]
#  [20]
#  [30]]

这样就能和 (5, 3) 的表格做"每列加不同值"(见第 8 章广播)。

小结

操作 作用 返回副本?
reshape 改形状(数量不变) 视图
flatten 展平一维 副本
ravel 展平一维 视图
.T / transpose 转置 / 重排轴 视图
newaxis / expand_dims 加长度为 1 的维度 视图
squeeze 删长度为 1 的维度 视图

牢记:reshape、transpose、ravel 都是视图,改它们会污染原数组——要独立副本就 .copy()。

下一章:对数组每个元素做数学函数(ufunc)。


上一篇:第 4 章 数组运算 | 下一篇:第 6 章 通用函数