NumPy 常用操作速查
NumPy 常用操作速查
Section titled “NumPy 常用操作速查”NumPy 的核心数据结构是多维数组 ndarray。这篇笔记整理科学计算和深度学习中常用的数组操作,重点记录形状变化、广播规则以及容易混淆的行为。
import numpy as np1. 数组基础
Section titled “1. 数组基础”1.1 创建数组
Section titled “1.1 创建数组”np.array([1, 2, 3]) # 从 Python 序列创建np.zeros((2, 3)) # 全 0 数组np.ones((2, 3)) # 全 1 数组np.full((2, 3), 7) # 使用指定值填充np.empty((2, 3)) # 只分配内存,不初始化值
np.arange(0, 10, 2) # [0, 2, 4, 6, 8]np.linspace(0, 1, 5) # 在 [0, 1] 内生成 5 个等距点
np.zeros_like(x) # 形状和 dtype 与 x 相同np.ones_like(x)np.full_like(x, 7)浮点数范围优先使用 np.linspace。np.arange 使用浮点步长时可能受浮点精度影响,终点也不一定符合直觉。
1.2 常用属性
Section titled “1.2 常用属性”x = np.zeros((2, 3, 4), dtype=np.float32)
x.shape # (2, 3, 4),各维度大小x.ndim # 3,维度数量x.size # 24,元素总数x.dtype # dtype('float32')x.itemsize # 4,单个元素占用的字节数x.nbytes # 96,数组元素占用的总字节数1.3 数据类型转换
Section titled “1.3 数据类型转换”x = np.array([1, 2, 3])y = x.astype(np.float32)astype 默认返回新数组。常用类型包括 np.float32、np.float64、np.int32、np.int64 和 np.bool_。
2. 索引与切片
Section titled “2. 索引与切片”2.1 基本索引
Section titled “2.1 基本索引”x = np.arange(12).reshape(3, 4)
x[0, 1] # 第 0 行、第 1 列的元素x[0] # 第 0 行,shape 为 (4,)x[:, 1] # 第 1 列,shape 为 (3,)x[-1] # 最后一行2.2 切片
Section titled “2.2 切片”切片写作 start:stop:step,区间遵循左闭右开规则,即包含 start、不包含 stop。
x[1:3] # 第 1、2 行x[:, ::2] # 所有行,每隔一列取一个元素x[::-1] # 反转第 0 维在图像或特征图中截取局部窗口:
# x.shape == (N, C, H, W)patch = x[:, :, i:i + h, j:j + w]其中 : 表示保留该维度的全部元素,返回结果仍是四维数组。
基本切片通常返回原数组的视图(view),修改切片可能同时修改原数组:
x = np.arange(5)y = x[1:3]y[0] = 100
x # array([0, 100, 2, 3, 4])需要独立数据时显式复制:
y = x[1:3].copy()2.3 布尔索引
Section titled “2.3 布尔索引”x = np.array([-2, -1, 0, 1, 2])
mask = x > 0x[mask] # array([1, 2])x[x < 0] = 0 # 将负数原地截断为 0直接使用布尔索引读取数据时,结果通常是一维副本。它常用于过滤无效值、实现掩码和条件赋值。
2.4 整数数组索引
Section titled “2.4 整数数组索引”x = np.array([10, 20, 30, 40])x[[3, 1, 1]] # array([40, 20, 20])整数数组索引属于高级索引,返回的是副本,而不是视图。
3. 形状变换
Section titled “3. 形状变换”3.1 reshape
Section titled “3.1 reshape”reshape 改变数组形状,但不改变元素的逻辑顺序。新形状的元素总数必须与原数组相同。
x = np.arange(12)
x.reshape(3, 4) # shape: (3, 4)x.reshape(3, -1) # 自动推断为 (3, 4)x.reshape(-1) # 展平为一维reshape 会尽量返回视图,但当内存布局不满足要求时也可能产生副本,因此不要依赖它一定共享内存。
3.2 增加和删除维度
Section titled “3.2 增加和删除维度”x = np.arange(6).reshape(2, 3) # shape: (2, 3)
x[None, ...].shape # (1, 2, 3)x[:, None, :].shape # (2, 1, 3)np.expand_dims(x, axis=0).shape # (1, 2, 3)
y = np.zeros((1, 2, 1, 3))np.squeeze(y).shape # (2, 3),删除所有大小为 1 的维度np.squeeze(y, axis=0).shape # (2, 1, 3),只删除指定维度不确定其他维度是否为 1 时,优先为 squeeze 指定 axis,避免误删维度。
3.3 转置与维度交换
Section titled “3.3 转置与维度交换”# x.shape == (N, H, W, C)y = x.transpose(0, 3, 1, 2) # NHWC -> NCHW
z = np.swapaxes(x, 1, 2) # 只交换第 1、2 维matrix_t = matrix.T # 二维矩阵转置transpose 需要给出所有轴的新顺序。它通常返回视图,不改变数据值,但会改变内存访问顺序。
注意:一维数组的 .T 不会变成列向量。
x = np.array([1, 2, 3])x.T.shape # (3,)x[:, None].shape # (3, 1),列向量x[None, :].shape # (1, 3),行向量3.4 拼接与堆叠
Section titled “3.4 拼接与堆叠”a = np.ones((2, 3))b = np.zeros((2, 3))
np.concatenate([a, b], axis=0).shape # (4, 3),沿已有轴拼接np.concatenate([a, b], axis=1).shape # (2, 6)np.stack([a, b], axis=0).shape # (2, 2, 3),创建新轴concatenate:沿已有维度连接,除连接轴外的维度必须相同。stack:创建一个新维度,所有输入数组的形状必须完全相同。
4. 广播机制
Section titled “4. 广播机制”广播允许 NumPy 在逐元素运算时自动扩展大小为 1 的维度,而不必真正复制数据。
4.1 广播规则
Section titled “4.1 广播规则”比较两个数组的形状时,从最后一个维度开始向前对齐。每组对应维度必须满足以下条件之一:
- 两个维度大小相同;
- 其中一个维度大小为
1; - 其中一个数组缺少该维度,此时按大小为
1处理。
例如:
(8, 1, 6, 1)( 7, 1, 5)-------------(8, 7, 6, 5)4.2 常见示例
Section titled “4.2 常见示例”二维数组按列添加偏置:
# x.shape == (N, D)# bias.shape == (D,)y = x + bias # bias 视为 (1, D)NCHW 特征图按通道添加偏置:
# x.shape == (N, C, H, W)# bias.shape == (C,)channel_bias = bias.reshape(1, C, 1, 1)y = x + channel_bias形状为 (C,) 的数组会从最后一维开始与 (N, C, H, W) 对齐,因此不能直接表示 NCHW 格式的通道偏置。
调试广播问题时可先查看目标形状:
np.broadcast_shapes((8, 1, 6, 1), (7, 1, 5))# (8, 7, 6, 5)5. 数值运算
Section titled “5. 数值运算”5.1 逐元素运算
Section titled “5.1 逐元素运算”a + ba - ba * ba / ba ** 2
np.maximum(a, b)np.minimum(a, b)np.exp(a)np.log(a)np.sqrt(a)np.abs(a)输入数组只要能够广播,就可以执行逐元素运算,输出形状是广播后的形状。
深度学习中的常见例子:
relu = np.maximum(x, 0)sigmoid = 1 / (1 + np.exp(-x))squared_error = (y_pred - y_true) ** 25.2 矩阵乘法
Section titled “5.2 矩阵乘法”c = a @ bc = np.matmul(a, b) # 与上式等价二维矩阵乘法要求内层维度相同:
(m, k) @ (k, n) -> (m, n)必须区分逐元素乘法和矩阵乘法:
a * b # 逐元素乘法a @ b # 矩阵乘法对于高维数组,matmul 将最后两个维度视为矩阵,其余维度按广播规则处理。
5.3 点积与通用求和
Section titled “5.3 点积与通用求和”np.dot(a, b)np.einsum("ik,kj->ij", a, b)np.dot 对高维数组的规则与 matmul 不同。表达矩阵乘法时优先使用 @;需要明确描述复杂维度收缩时使用 np.einsum。
6. 归约与统计
Section titled “6. 归约与统计”归约操作沿指定轴汇总数据:
x.sum(axis=0)x.mean(axis=1)x.max(axis=-1)x.min(axis=-1)x.argmax(axis=1)x.std(axis=0)x.var(axis=0)axis 表示执行归约的维度。默认情况下该维度会从结果中消失;设置 keepdims=True 可以保留大小为 1 的维度,便于后续广播。
# x.shape == (N, C, H, W)channel_mean = x.mean(axis=(0, 2, 3), keepdims=True)# channel_mean.shape == (1, C, 1, 1)
x_centered = x - channel_mean多个轴可以通过元组一次归约:
spatial_mean = x.mean(axis=(2, 3)) # shape: (N, C)含有缺失值 NaN 时,可以使用忽略 NaN 的版本:
np.nanmean(x)np.nansum(x)np.nanmax(x)7. 条件选择与掩码
Section titled “7. 条件选择与掩码”7.1 where
Section titled “7.1 where”result = np.where(condition, value_if_true, value_if_false)
relu = np.where(x > 0, x, 0)只传入条件时,np.where(condition) 返回满足条件的元素索引;若只是查找索引,使用 np.nonzero(condition) 语义更清晰。
7.2 多条件组合
Section titled “7.2 多条件组合”NumPy 数组的条件需要使用 &、| 和 ~,并为每个比较表达式添加括号:
mask = (x >= 0) & (x <= 1)selected = x[mask]不能使用 Python 的 and、or 或链式比较 0 <= x <= 1。
7.3 裁剪数值范围
Section titled “7.3 裁剪数值范围”clipped = np.clip(x, 0, 1)当目标只是限制上下界时,np.clip 比嵌套使用 minimum、maximum 或 where 更直接。
8. 排序与查找
Section titled “8. 排序与查找”np.sort(x, axis=-1) # 返回排序后的值np.argsort(x, axis=-1) # 返回排序索引np.argmax(x, axis=-1) # 最大值索引np.argmin(x, axis=-1) # 最小值索引np.unique(x) # 去重并排序np.nonzero(x) # 非零元素索引获取一维数组中最大的 k 个元素时,argpartition 通常比完整排序更高效:
k = 3indices = np.argpartition(x, -k)[-k:]top_k = x[indices] # 这 k 个元素内部不保证有序9. 随机数
Section titled “9. 随机数”新代码推荐使用独立的随机数生成器,便于控制和复现实验:
rng = np.random.default_rng(seed=42)
rng.random((2, 3)) # [0, 1) 均匀分布rng.normal(0, 1, (2, 3)) # 正态分布rng.integers(0, 10, size=5) # [0, 10) 内的随机整数rng.permutation(10) # 0 到 9 的随机排列10. 数值检查与调试
Section titled “10. 数值检查与调试”np.isnan(x) # 判断元素是否为 NaNnp.isinf(x) # 判断元素是否为正无穷或负无穷np.isfinite(x) # 判断元素是否为有限值结合 any 可以快速判断整个数组是否存在异常值:
if not np.isfinite(x).all(): raise ValueError("x contains NaN or infinity")比较浮点数组时不要直接使用 ==,应使用带容差的比较:
np.isclose(a, b)np.allclose(a, b)在测试中可以使用:
np.testing.assert_allclose(actual, expected, rtol=1e-5, atol=1e-8)11. 常见场景
Section titled “11. 常见场景”11.1 标准化
Section titled “11.1 标准化”# 按最后一维标准化mean = x.mean(axis=-1, keepdims=True)std = x.std(axis=-1, keepdims=True)x_normalized = (x - mean) / (std + 1e-8)11.2 稳定计算 Softmax
Section titled “11.2 稳定计算 Softmax”先减去最大值可以避免 exp 上溢:
shifted = logits - logits.max(axis=-1, keepdims=True)exp_values = np.exp(shifted)probabilities = exp_values / exp_values.sum(axis=-1, keepdims=True)11.3 One-hot 编码
Section titled “11.3 One-hot 编码”labels = np.array([2, 0, 1])one_hot = np.eye(3, dtype=np.float32)[labels]
# one_hot ==# [[0., 0., 1.],# [1., 0., 0.],# [0., 1., 0.]]11.4 批量线性变换
Section titled “11.4 批量线性变换”# x.shape == (batch, sequence, d_in)# weight.shape == (d_in, d_out)# bias.shape == (d_out,)y = x @ weight + bias# y.shape == (batch, sequence, d_out)11.5 二维卷积的局部计算
Section titled “11.5 二维卷积的局部计算”下面这一行组合了切片、逐元素乘法和归约,是二维卷积单个位置的核心计算:
# region.shape == kernel.shape == (C, kernel_h, kernel_w)output[i, j] = (region * kernel).sum() + bias完整实现还需要处理批次、多个输出通道、步长和填充。
12. 视图、副本与原地修改
Section titled “12. 视图、副本与原地修改”是否共享内存会影响正确性和性能:
- 基本切片通常返回视图;
- 布尔索引和整数数组索引返回副本;
reshape、transpose可能返回共享内存的视图;copy()明确创建独立副本。
可以用下面的方法辅助判断:
np.shares_memory(a, b)带 out 参数或对切片赋值会修改原数组,应避免在仍需使用原始数据时误操作:
np.add(a, b, out=a) # 将结果写回 ax[x < 0] = 0 # 原地修改 x13. 常用 API 速查表
Section titled “13. 常用 API 速查表”| 需求 | 推荐写法 |
|---|---|
| 查看形状 | x.shape |
| 改变形状 | x.reshape(...) |
| 展平数组 | x.reshape(-1) 或 x.ravel() |
| 复制数据 | x.copy() |
| 调整轴顺序 | x.transpose(...) |
| 增加维度 | x[None, ...] 或 np.expand_dims(x, axis) |
| 删除大小为 1 的维度 | np.squeeze(x, axis) |
| 沿已有轴拼接 | np.concatenate([...], axis=...) |
| 沿新轴堆叠 | np.stack([...], axis=...) |
| 逐元素乘法 | a * b |
| 矩阵乘法 | a @ b |
| 按轴求和或均值 | x.sum(axis=...)、x.mean(axis=...) |
| 保留归约维度 | keepdims=True |
| 条件选择 | np.where(condition, a, b) |
| 限制数值范围 | np.clip(x, lower, upper) |
| 查找排序索引 | np.argsort(x) |
| 判断能否广播 | np.broadcast_shapes(...) |
| 检查异常值 | np.isfinite(x).all() |
| 浮点近似比较 | np.allclose(a, b) |
遇到形状错误时,优先打印参与运算数组的 shape,然后按照“从右向左对齐”的规则逐维检查。多数 NumPy 维度问题都可以通过这一步快速定位。