NumPy
一、速查🧀
下面各行独立查阅。示例中的 a、b 是数组,axis=-1 表示最后一根轴。
1、创建🧀
| 操作 | 写法 | 说明 |
|---|---|---|
| 从序列创建 | np.array([1, 2, 3]) | 从列表或元组创建数组 |
| 指定类型 | np.array([1, 2], dtype=np.float32) | 创建 float32 数组 |
| 尽量复用数据 | np.asarray(data) | 输入已是合适数组时避免复制 |
| 全零 / 全一 | np.zeros((2, 3)) / np.ones((2, 3)) | 形状为 (2, 3) |
| 指定填充值 | np.full((2, 3), 5) | 所有元素为 5 |
| 单位矩阵 | np.eye(3) | 形状为 (3, 3) |
| 等差序列 | np.arange(0, 10, 2) | 左闭右开,得到 0, 2, 4, 6, 8 |
| 等距取点 | np.linspace(0, 1, 5) | 包含两端,共取 5 个点 |
| 网格坐标 | np.meshgrid(x, y, indexing="xy") | 根据坐标轴生成网格 |
| 参照数组 | np.zeros_like(a) / np.ones_like(a) | 默认继承形状和类型 |
| 随机生成器 | rng = np.random.default_rng(42) | 推荐的随机数入口 |
| 均匀随机数 | rng.random((2, 3)) | 范围 [0, 1) |
| 标准正态 | rng.standard_normal((2, 3)) | 均值 0、方差 1 |
| 随机整数 | rng.integers(0, 10, size=(2, 3)) | 范围 [0, 10) |
Tip
浮点等差序列更适合用 linspace。arange(0, 1, 0.1) 受浮点误差影响,元素个数和末尾值可能不符合直觉。
2、属性🧀
| 操作 | 写法 | 说明 |
|---|---|---|
| 形状 | a.shape | 各轴长度组成的元组 |
| 维数 | a.ndim | 轴的数量 |
| 元素数 | a.size | 所有轴长度的乘积 |
| 数据类型 | a.dtype | 如 int64、float32 |
| 单元素字节数 | a.itemsize | 每个元素占用的字节数 |
| 总字节数 | a.nbytes | 数组数据占用的字节数 |
| 步长 | a.strides | 沿各轴前进一格跨过的字节数 |
| 转置 | a.T | 反转全部轴;二维时交换行列 |
3、索引🧀
| 操作 | 写法 | 说明 |
|---|---|---|
| 取行 / 列 | a[0] / a[:, 0] | 对二维数组取第一行、第一列 |
| 切片 | a[:, 1:3] | 取第 1、2 列 |
| 保留维度 | a[0:1] | 取第一项,但保留第 0 轴 |
| 反向 | a[::-1] | 沿第一轴倒序 |
| 间隔取值 | a[::2] | 每隔一个取一个 |
| 指定下标 | a[[0, 2]] | 花式索引,取第 0、2 项 |
| 逐项坐标 | a[[0, 1], [2, 0]] | 取 a[0, 2] 和 a[1, 0] |
| 布尔筛选 | a[a > 0] | 返回所有正元素的一维数组 |
| 条件选择 | np.where(a > 0, a, 0) | 正数保留,其余置零 |
| 非零坐标 | np.argwhere(a != 0) | 每行是一组坐标 |
| 按轴取值 | np.take(a, indices, axis=0) | 沿指定轴取多个位置 |
| 逐行取值 | np.take_along_axis(a, idx, axis=1) | idx 与结果形状一致 |
Tip
多个条件组合时使用 &、|、~,每个比较加括号,如 (a > 0) & (a < 1);不要使用 Python 的 and、or。
4、变形🧀
| 操作 | 写法 | 说明 |
|---|---|---|
| 改变形状 | a.reshape(2, -1) | 自动推算一维,元素总数不变 |
| 展平 | a.ravel() | 尽可能返回视图 |
| 复制展平 | a.flatten() | 总是返回副本 |
| 增加维度 | np.expand_dims(a, axis=0) | 在第 0 轴插入长度 1 |
| 简写增维 | a[:, None] | 在最后增加一根轴 |
| 删除单维 | np.squeeze(a, axis=1) | 第 1 轴长度必须为 1 |
| 交换两轴 | np.swapaxes(a, 0, 1) | 交换指定轴 |
| 调整轴序 | a.transpose(0, 2, 1) | 指定新的轴顺序 |
| 移动轴 | np.moveaxis(a, 0, -1) | 将第 0 轴移到最后 |
| 广播视图 | np.broadcast_to(a, (4, 3)) | 广播到目标形状,通常只读 |
| 逐元素重复 | np.repeat(a, 3, axis=0) | 每个元素沿轴重复 |
| 整体平铺 | np.tile(a, (2, 3)) | 将整个数组平铺 |
5、拼接🧀
| 操作 | 写法 | 说明 |
|---|---|---|
| 沿原轴拼接 | np.concatenate([a, b], axis=0) | 除拼接轴外,其余形状相同 |
| 沿新轴堆叠 | np.stack([a, b], axis=0) | 输入数组形状必须相同 |
| 纵向拼接 | np.vstack([a, b]) | 对二维数组沿行拼接 |
| 横向拼接 | np.hstack([a, b]) | 对二维数组沿列拼接 |
| 组成列 | np.column_stack([x, y]) | 将一维数组作为列 |
| 平均切分 | np.split(a, 3, axis=0) | 必须能够等分 |
| 非平均切分 | np.array_split(a, 3, axis=0) | 允许各段长度不同 |
6、运算🧀
| 操作 | 写法 | 说明 |
|---|---|---|
| 逐元素运算 | a + b、a - b、a * b、a / b | 形状相同或可以广播 |
| 整除 / 余数 | a // b / a % b | 逐元素计算 |
| 幂 / 开方 | a ** 2 / np.sqrt(a) | 开方注意取值范围 |
| 指数 / 对数 | np.exp(a) / np.log(a) | 实数对数要求输入为正 |
| 绝对值 | np.abs(a) | 逐元素绝对值 |
| 截断范围 | np.clip(a, 0, 1) | 限制在 [0, 1] |
| 四舍五入 | np.round(a, 2) | 保留两位小数 |
| 逐元素极值 | np.maximum(a, b) / np.minimum(a, b) | 比较对应元素 |
| 矩阵乘法 | a @ b / np.matmul(a, b) | 也支持批次广播 |
| 向量点积 | np.vdot(a, b) | 展平后计算,复数会共轭第一个参数 |
| 外积 | np.outer(a, b) | 两个向量的外积 |
| 爱因斯坦求和 | np.einsum("bnd,bd->bn", a, b) | 按下标描述乘法和求和 |
7、统计🧀
| 操作 | 写法 | 说明 |
|---|---|---|
| 求和 / 均值 | a.sum(axis=0) / a.mean(axis=0) | 沿指定轴归约 |
| 保留轴 | a.mean(axis=0, keepdims=True) | 被归约的轴保留为长度 1 |
| 极值 | a.min(axis=0) / a.max(axis=0) | 沿指定轴取极值 |
| 极值下标 | a.argmin(axis=0) / a.argmax(axis=0) | 返回位置 |
| 方差 / 标准差 | a.var(ddof=0) / a.std(ddof=0) | 默认除以 N |
| 中位数 | np.median(a, axis=0) | 对异常值更稳健 |
| 分位数 | np.quantile(a, [0.25, 0.5, 0.75]) | 计算指定分位点 |
| 累加 / 累乘 | a.cumsum(axis=0) / a.cumprod(axis=0) | 沿轴累计 |
| 排序 | np.sort(a, axis=-1) | 返回排好序的副本 |
| 排序下标 | np.argsort(a, axis=-1) | 返回排序后的原下标 |
| 前 k 小 | np.partition(a, k - 1)[:k] | 不保证前 k 项内部有序 |
| 去重 | np.unique(a) | 默认返回排好序的唯一值 |
| 计数 | values, counts = np.unique(a, return_counts=True) | 唯一值及出现次数 |
| 任意 / 全部 | (a > 0).any() / (a > 0).all() | 逻辑归约 |
8、判断🧀
| 操作 | 写法 | 说明 |
|---|---|---|
| 逐元素比较 | a == b | 返回布尔数组 |
| 数组完全相同 | np.array_equal(a, b) | 形状和元素均相同 |
| 浮点近似相同 | np.allclose(a, b) | 允许相对和绝对误差 |
| 是否含 NaN | np.isnan(a) | 逐元素检查 |
| 是否为有限值 | np.isfinite(a) | 排除 NaN 和正负无穷 |
| 忽略 NaN 求均值 | np.nanmean(a, axis=0) | 全为 NaN 时会警告并返回 NaN |
| 替换异常值 | np.nan_to_num(a) | 替换 NaN 和正负无穷 |
| 集合包含 | np.isin(a, values) | 判断元素是否在集合中 |
9、转换🧀
| 操作 | 写法 | 说明 |
|---|---|---|
| 改类型 | a.astype(np.float32) | 通常返回新数组 |
| 转列表 | a.tolist() | 转成嵌套 Python 列表 |
| 取标量 | a.item() | 数组只能有一个元素 |
| 复制 | a.copy() | 复制数组和底层数据 |
写入 .npy | np.save("data.npy", a) | 保存单个数组 |
读取 .npy | np.load("data.npy", allow_pickle=False) | 读取单个数组 |
写入 .npz | np.savez_compressed("data.npz", x=a, y=b) | 保存多个命名数组并压缩 |
| 文本写入 | np.savetxt("data.csv", a, delimiter=",") | 保存规则二维数值表 |
| 文本读取 | np.loadtxt("data.csv", delimiter=",") | 读取规则数值文本 |
二、数组🧀
1、形状🧀
ndarray 是同类型元素组成的多维数组。标量、向量和矩阵都只是不同维数的数组。
| 数据 | 常见形状 | 含义 |
|---|---|---|
| 标量 | () | 单个值 |
| 向量 | (D,) | D 个元素 |
| 表格 | (N, D) | N 行、D 列 |
| 图像 | (H, W, C) | 高、宽、通道 |
| 批次 | (N, H, W, C) | 批次、高、宽、通道 |
数组必须是规则的长方体。各行长度不同的嵌套列表不能直接构成普通数值数组,应先补齐、拆开保存,或明确使用 dtype=object。
2、类型🧀
| 类型 | 常见用途 |
|---|---|
np.bool_ | 掩码、条件判断 |
np.int32 / np.int64 | 整数、类别编号 |
np.float32 | 机器学习、较省内存的数值计算 |
np.float64 | NumPy 默认浮点类型、一般科学计算 |
np.complex64 / np.complex128 | 复数计算 |
np.str_ | 定长 Unicode 字符串 |
整数和浮点数混合运算时,NumPy 会根据类型提升规则决定结果类型。内存和精度敏感时,应显式指定 dtype。
Warning
整数类型范围有限,会发生溢出。e.g. 图像常见的 uint8 只能表示 0 到 255,做减法或乘法前通常要先转换成更宽的整数或浮点类型。
3、轴🧀
axis 指被归约或操作的轴。二维数组形状为 (行, 列):
axis=0:消去行轴,对每一列计算;axis=1:消去列轴,对每一行计算。
均值
keepdims=True 会把被消去的轴保留为长度 1,便于后续广播。
4、副本🧀
赋值、切片、花式索引的内存行为不同:
| 操作 | 结果 |
|---|---|
b = a | 同一个数组的另一个变量名 |
b = a[1:3] | 基本切片通常是视图,共享数据 |
b = a[[1, 3]] | 花式索引返回副本 |
b = a[a > 0] | 布尔索引返回副本 |
b = a.copy() | 显式创建独立副本 |
b = a.reshape(...) | 可能是视图,也可能是副本 |
视图
可以用 np.shares_memory(a, b) 检查两个数组是否确定共享内存。准备修改数据且不想影响原数组时,直接使用 copy()。
三、索引🧀
1、切片🧀
整数索引会消去对应轴,切片会保留轴。a[0] 的形状是 (4,),a[0:1] 的形状是 (1, 4)。
2、花式🧀
整数数组可以一次取多个位置。
a[[0, 2], [1, 3]] 是逐对取坐标;想取行列组合形成的矩形区域,使用 np.ix_。
3、掩码🧀
a[mask]:筛选元素,结果通常变成一维;np.where(condition, x, y):在相同位置二选一,保留广播后的形状;np.where(condition):返回各轴下标组成的元组,查坐标时argwhere更直观。
4、赋值🧀
布尔掩码赋值会直接修改原数组。右侧可以是标量,也可以是与选中元素数量匹配的数组。
Warning
链式索引容易产生临时副本。要修改原数组,优先写成一次索引,如 a[rows, cols] = value。
四、形状🧀
1、变形🧀
reshape 改形状,transpose 改轴序。 e.g. 图像从 HWC 改为 CHW 应使用 a.transpose(2, 0, 1),不能直接 reshape。
-1 最多出现一次,由 NumPy 根据元素总数自动推算。reshape 不保证一定复制或共享数据,不应依赖其内存行为。
2、增减🧀
None 与 np.newaxis 等价。squeeze 只删除长度为 1 的轴:
Warning
直接 squeeze() 会删除所有长度为 1 的轴。若某一轴有固定含义,明确指定 axis。
3、拼接🧀
拼接与堆叠
concatenate 沿已有轴接起来;stack 创建一根新轴。后两项形状虽然相同,新轴的位置和含义并不相同。
4、广播🧀
两个数组逐元素运算时,从最右侧开始比较形状。对应维度满足以下任意条件即可:
- 长度相同;
- 其中一个长度为
1; - 某一边没有该维度,按
1处理。
标准化
(3, 2) 与 (1, 2) 可以广播,均值和标准差会作用于每一行。
(3, 2) 与 (3,) 不能广播,因为最右侧的 2 与 3 不兼容。若要给每行加一个值,可将后者变为 (3, 1)。
广播通常不会真的复制较小数组,但结果本身仍可能很大。e.g. (10000, 1, 3) 与 (1, 10000, 3) 相减会产生 (10000, 10000, 3) 的结果,应留意内存。
五、运算🧀
1、向量化🧀
NumPy 的核心写法是让运算作用于整个数组。
这比逐个元素执行 Python 循环更简洁,通常也更快。可以广播或用 ufunc 表达的操作,优先写成数组运算。
2、ufunc🧀
np.add、np.sqrt、np.exp 等是通用函数,即 ufunc。它们支持广播、类型处理和按轴归约。
遇到除零、溢出或非法值时,可以临时设置处理方式:
3、排序🧀
若只需要第 k 小或前 k 小,不必完整排序:
4、缺失值🧀
浮点数组常用 NaN 表示缺失值。
NaN 不等于自身,因此不要用 a == np.nan 判断。整数数组不能直接保存 NaN,需要转为浮点,或另建布尔掩码记录缺失位置。
六、随机🧀
新代码推荐创建独立的 Generator:
| 操作 | 写法 |
|---|---|
[0, 1) 均匀分布 | rng.random((2, 3)) |
[low, high) 均匀分布 | rng.uniform(low, high, size) |
| 标准正态分布 | rng.standard_normal(size) |
| 一般正态分布 | rng.normal(mean, std, size) |
| 随机整数 | rng.integers(low, high, size) |
| 伯努利试验 | rng.binomial(1, p, size) |
| 随机选择 | rng.choice(a, size=3, replace=False) |
| 原地打乱 | rng.shuffle(a) |
| 返回乱序副本 | rng.permutation(a) |
划分数据
相同的种子和调用顺序通常产生相同序列,便于复查实验。不要在函数内部反复使用同一个种子重新创建生成器,否则每次都可能得到完全相同的样本。
七、线代🧀
1、乘法🧀
matmul 对最后两维做矩阵乘法,前面的维度作为批次维并参与广播。
2、方程🧀
线性方程 \(Ax=b\) 使用 solve:
已知右侧向量时,直接求解比先计算 np.linalg.inv(A) @ b 更准确、更高效。
3、分解🧀
| 操作 | 写法 | 说明 |
|---|---|---|
| 范数 | np.linalg.norm(a) | 默认计算二维 Frobenius 范数 |
| 行列式 | np.linalg.det(a) | 方阵行列式 |
| 矩阵秩 | np.linalg.matrix_rank(a) | 数值秩 |
| 特征分解 | values, vectors = np.linalg.eig(a) | 一般方阵 |
| 对称特征分解 | values, vectors = np.linalg.eigh(a) | 实对称或复 Hermitian 矩阵 |
| 奇异值分解 | u, s, vh = np.linalg.svd(a, full_matrices=False) | \(A=U\Sigma V^H\) |
| 最小二乘 | coef, *_ = np.linalg.lstsq(X, y, rcond=None) | 求过定或欠定系统的解 |
对实对称矩阵优先使用 eigh,它利用矩阵结构,返回实特征值并且通常更稳定。
八、读写🧀
1、二进制🧀
NumPy 自有格式会保留形状和数据类型。
多个数组可以存进同一个文件:
2、文本🧀
loadtxt:适合字段规则、没有缺失值的纯数值文本;genfromtxt:可以处理缺失值,但规则较复杂;- 表头、混合类型、日期等表格数据通常更适合使用 pandas。
3、打印🧀
precision 控制显示精度,不会改变数组中的真实数值;suppress=True 尽量不用科学计数法显示较小数字。
九、实例🧀
标准化
对每个特征分别做标准化,并处理标准差为 0 的常量列。
常量列减去均值后全为 0,令其除数为 1 可以避免除零。真实机器学习任务中,均值和标准差只用训练集计算,再应用到验证集和测试集。
距离
计算每个点到每个中心的平方欧氏距离,并找到最近中心。
这里两次增加维度,让 (N, 1, D) 与 (1, K, D) 广播成 (N, K, D)。当 N、K 很大时,中间数组也会很大,需要分块计算。
回归
用最小二乘拟合 \(y=wx+b\)。
column_stack 将输入和常数 1 组成设计矩阵;lstsq 直接求最小二乘解,不需要手动计算逆矩阵。