02 · NumPy 数值计算基础
掌握 NumPy 数组的创建、索引、运算、变形与随机数生成。网络药理学中的表达矩阵、成分-靶点矩阵本质上都是 NumPy 数组,务必熟练掌握。
0. 小白导读:NumPy 是什么?为什么需要它?
1. 为什么需要 NumPy
Python 自带的列表(list)也能存数字,但有两个致命问题:
- 慢:列表存储的是 Python 对象,计算时要逐个解释,速度远慢于 C 语言。
- 不能做真正的数学运算:
[1,2,3] * 2得到[1,2,3,1,2,3](重复),而不是[2,4,6]。
NumPy 用 C 语言实现底层,提供多维数组(ndarray),支持:
- 向量化运算(整个数组一次算完,快几十倍)
- 广播机制(不同形状的数组也能运算)
- 丰富的数学、统计、线性代数函数
一句话:NumPy 数组 = 高性能的"矩阵",是 Pandas 和 scikit-learn 的地基。
import numpy as np # 对比:列表乘法 vs 数组乘法 lst = [1, 2, 3] arr = np.array([1, 2, 3]) print(lst * 2) # [1, 2, 3, 1, 2, 3](重复,不是数学运算) print(arr * 2) # [2 4 6](真正的逐元素乘法)
2. 数组创建
2.1 从列表创建
import numpy as np # 一维数组 a = np.array([1, 2, 3]) print(a) # [1 2 3] # 二维数组(矩阵) b = np.array([[1, 2, 3], [4, 5, 6]]) print(b) # [[1 2 3] # [4 5 6]] # 指定数据类型 c = np.array([1, 2, 3], dtype=float) print(c) # [1. 2. 3.]
2.2 常用创建函数
# 全零 / 全一 / 单位矩阵 np.zeros((2, 3)) # 2行3列全0 np.ones((3, 2)) # 3行2列全1 np.eye(3) # 3x3 单位矩阵(对角线为1) # 等差序列 np.arange(5) # [0 1 2 3 4] np.arange(1, 10, 2) # [1 3 5 7 9](起始、结束、步长) # 等分序列(含端点,共 n 个点) np.linspace(0, 1, 5) # [0. 0.25 0.5 0.75 1. ] # 随机数组 np.random.rand(2, 3) # [0,1) 均匀分布 np.random.randn(2, 3) # 标准正态分布 np.random.randint(0, 10, 5) # [0,10) 随机整数,5个
科研场景:np.zeros 常用于初始化结果矩阵;np.linspace 常用于生成浓度梯度、时间点序列。
3. 数组属性
arr = np.array([[1, 2, 3], [4, 5, 6]]) arr.shape # (2, 3) 形状(行数, 列数) arr.ndim # 2 维度数 arr.size # 6 元素总数 arr.dtype # dtype('int64') 数据类型
️ shape 是属性不是方法,不要写成 arr.shape()。
4. 索引与切片
4.1 一维数组
a = np.array([10, 20, 30, 40, 50]) a[0] # 10(第1个) a[-1] # 50(最后1个) a[1:4] # [20 30 40](左闭右开:索引1到3) a[:3] # [10 20 30](前3个) a[::2] # [10 30 50](隔一个取一个)
4.2 二维数组
b = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) b[0, 1] # 2(第0行第1列) b[1] # [4 5 6](第1行) b[:, 0] # [1 4 7](所有行的第0列) b[0:2, 1:3] # 子矩阵 [[2 3],[5 6]]
记住口诀:[行, 列],行在前列在后,切片左闭右开。
4.3 布尔索引(科研高频,重点)
data = np.array([3.2, 5.8, 1.5, 7.1, 2.4]) # 筛选大于4的值 mask = data > 4 print(mask) # [False True False True False] print(data[mask]) # [5.8 7.1] # 一行写法 print(data[data > 4]) # [5.8 7.1] # 组合条件 print(data[(data > 2) & (data < 6)]) # [3.2 5.8 2.4]
️ 组合条件用 &(与)、|(或)、~(非),不要用 Python 的 and / or(那是对单个布尔值用的)。
4.4 花式索引(整数数组索引)
a = np.array([10, 20, 30, 40, 50]) idx = [0, 2, 4] print(a[idx]) # [10 30 50](按索引列表取值)
5. 数组运算与广播机制
5.1 逐元素运算
a = np.array([1, 2, 3]) b = np.array([4, 5, 6]) a + b # [5 7 9] a - b # [-3 -3 -3] a * b # [4 10 18](逐元素乘,不是矩阵乘法!) a / b # [0.25 0.4 0.5 ] a ** 2 # [1 4 9]
5.2 矩阵乘法(重点区分)
A = np.array([[1, 2], [3, 4]]) B = np.array([[5, 6], [7, 8]]) # 逐元素乘法(对应位置相乘) print(A * B) # [[ 5 12] # [21 32]] # 真正的矩阵乘法(行×列) print(np.dot(A, B)) # 或 A @ B # [[19 22] # [43 50]]
* 是逐元素乘,@ / np.dot 是矩阵乘法。网络药理学中计算成分-靶点矩阵乘积时,一定要用 @。
5.3 广播机制(重点)
广播规则:从最后一个维度开始对齐,维度相等或其中一个为 1 就可以运算。
# 标量与数组 arr = np.array([1, 2, 3]) print(arr + 10) # [11 12 13] # 一维数组与二维数组(行广播) matrix = np.array([[1, 2, 3], [4, 5, 6]]) row = np.array([10, 20, 30]) print(matrix + row) # [[11 22 33] # [14 25 36]] # 列广播(需要 reshape 成列向量) col = np.array([10, 20]).reshape(2, 1) print(matrix + col) # [[11 12 13] # [24 25 26]]
科研场景:对表达矩阵做标准化(每列减去均值、除以标准差)就是广播的典型应用:
> data = np.random.randn(5, 4) # 5个样本 × 4个基因 > col_mean = data.mean(axis=0) # 每列均值(shape=(4,)) > col_std = data.std(axis=0) # 每列标准差 > zscore = (data - col_mean) / col_std # 广播自动对齐 >
6. 常用统计函数
data = np.array([[1, 2, 3], [4, 5, 6]]) data.mean() # 3.5(全部均值) data.mean(axis=0) # [2.5 3.5 4.5](每列均值,axis=0 沿行方向压缩) data.mean(axis=1) # [2. 5.](每行均值,axis=1 沿列方向压缩) data.sum() # 21 data.min() # 1 data.max() # 6 data.std() # 1.7078(标准差) data.var() # 2.9167(方差) np.exp(data) # 指数 np.log(data) # 自然对数 np.sqrt(data) # 平方根 np.abs(data) # 绝对值
axis 记忆法:axis=0 表示"跨行"(即对每一列做操作),axis=1 表示"跨列"(对每一行做操作)。不确定时先打印 shape 验证。
7. 数组变形与拼接
7.1 变形
a = np.arange(12) # [0 1 2 ... 11] a.reshape(3, 4) # 3行4列 a.reshape(2, 6) # 2行6列 a.reshape(2, -1) # -1 自动推断:2行6列 a.ravel() # 展平成一维(返回视图) a.flatten() # 展平成一维(返回新数组)
️ reshape 要求元素总数不变。ravel 返回的是视图(改它会影响原数组),flatten 返回副本。不确定时用 flatten 更安全。
7.2 拼接
a = np.array([1, 2, 3]) b = np.array([4, 5, 6]) np.concatenate([a, b]) # [1 2 3 4 5 6](一维拼接) m1 = np.array([[1, 2], [3, 4]]) m2 = np.array([[5, 6], [7, 8]]) np.vstack([m1, m2]) # 纵向拼接(行方向) # [[1 2] # [3 4] # [5 6] # [7 8]] np.hstack([m1, m2]) # 横向拼接(列方向) # [[1 2 5 6] # [3 4 7 8]]
科研场景:把多个样本的表达向量 vstack 成表达矩阵;把基因名列表与表达矩阵 hstack 合并。
8. 随机数与随机种子
8.1 随机数生成
# 固定形状随机数组 np.random.rand(3) # [0,1) 均匀分布,3个 np.random.randn(3) # 标准正态分布,3个 # 正态分布(指定均值和标准差) np.random.normal(loc=5, scale=2, size=(3, 3)) # 均值5,标准差2 # 随机整数 np.random.randint(0, 10, size=5) # 从数组中随机抽取 data = np.array([1, 2, 3, 4, 5]) np.random.choice(data, size=3, replace=False) # 不放回抽3个
8.2 随机种子(科研必备)
np.random.seed(42) # 固定随机种子 a = np.random.randn(3) np.random.seed(42) # 再次设置同样的种子 b = np.random.randn(3) print(a == b) # [ True True True] —— 结果可复现!
️ 为什么科研必须设随机种子? 机器学习、随机抽样、模拟实验都依赖随机数。不设种子,每次运行结果不同,无法复现实验——这在科研中是不可接受的。所有涉及随机的代码,开头都写 np.random.seed(42)(42 是约定俗成的幸运数字,也可用其他整数)。
9. 补充:科研高频操作
9.1 条件筛选 np.where
data = np.array([3.2, 5.8, 1.5, 7.1]) # 满足条件取第一个值,否则取第二个值 result = np.where(data > 4, "high", "low") print(result) # ['low' 'high' 'low' 'high']
9.2 唯一值与排序
arr = np.array([3, 1, 2, 3, 1]) np.unique(arr) # [1 2 3](去重并排序) np.sort(arr) # [1 1 2 3 3](排序) np.argsort(arr) # [1 4 2 0 3](排序后的索引,可用于重排其他数组)
9.3 缺失值处理
data = np.array([1.0, np.nan, 3.0, np.nan]) np.isnan(data) # [False True False True](判断缺失) data[~np.isnan(data)] # [1. 3.](去掉缺失) np.nanmean(data) # 2.0(忽略缺失算均值) np.nanstd(data) # 忽略缺失算标准差
️ np.mean(data) 遇到 nan 会返回 nan,必须用 np.nanmean 等 nan 版本函数。
9.4 线性代数 np.linalg
A = np.array([[1, 2], [3, 4]]) np.linalg.inv(A) # 矩阵求逆 np.linalg.det(A) # 行列式 np.linalg.eig(A) # 特征值、特征向量 np.linalg.norm(A) # 范数
网络药理学中矩阵运算(如邻接矩阵、特征分解)会用到,了解即可。
9.5 文件保存与加载
arr = np.array([[1, 2], [3, 4]]) # 保存为 .npy(二进制,保留数据类型) np.save("arr.npy", arr) loaded = np.load("arr.npy") # 保存为文本(可被 Excel / Pandas 读取) np.savetxt("arr.csv", arr, delimiter=",", fmt="%.3f") loaded_txt = np.loadtxt("arr.csv", delimiter=",")
10. 常见坑
| 坑 | 说明 | 正确做法 |
|---|---|---|
* 与 @ 混淆 |
逐元素乘 vs 矩阵乘 | 矩阵乘法用 A @ B |
布尔组合用 and |
数组布尔索引报错 | 用 & / | / ~ |
| 忘记设随机种子 | 结果不可复现 | np.random.seed(42) |
nan 污染统计 |
均值变 nan |
用 np.nanmean 等 |
shape 当方法调用 |
arr.shape() 报错 |
arr.shape 是属性 |
| 广播维度不匹配 | 报错 | 先打印 shape 检查 |
| 忘记 axis | 结果维度不对 | 明确 axis=0 / axis=1 |
11. 练习与交付物
交付物(必做)
模拟数据矩阵分析:用 NumPy 生成 10×5 的模拟数据矩阵(10 个样本 × 5 个基因,标准正态分布),完成:
- 设置随机种子
np.random.seed(42),生成np.random.randn(10, 5)。 - 打印矩阵的
shape、dtype、size。 - 计算每个基因(每列)的均值、标准差。
- 筛选出所有基因均值 > 0 的样本行(布尔索引)。
- 把矩阵标准化(每列减均值除标准差,用广播)。
- 保存标准化矩阵为
normalized_matrix.csv。
进阶练习
- 用
np.linspace(0, 100, 21)生成 21 个浓度点,计算np.exp(-0.05 * x)模拟释放曲线数据。 - 用
np.random.choice从 20 个基因中不放回抽 8 个,模拟随机抽样。 - 用
np.linalg计算一个 3×3 矩阵的特征值,验证特征值之和等于矩阵的迹(对角线之和)。 - 模拟 10000 次"抛硬币"(
np.random.randint(0, 2, 10000)),计算正面比例,观察它是否接近 0.5(中心极限定理的直观体验)。
12. 速查表
| 需求 | 代码 |
|---|---|
| 创建数组 | np.array([...]) |
| 全零/全一 | np.zeros(shape) / np.ones(shape) |
| 等差/等分 | np.arange(start, stop, step) / np.linspace(a, b, n) |
| 随机种子 | np.random.seed(42) |
| 正态随机 | np.random.normal(loc, scale, size) |
| 形状 | arr.shape / arr.reshape(r, c) |
| 索引 | arr[行, 列] |
| 布尔筛选 | arr[arr > 4] |
| 逐元素乘 | arr1 * arr2 |
| 矩阵乘 | arr1 @ arr2 |
| 统计 | arr.mean(axis=0) / arr.std() |
| 拼接 | np.vstack([a, b]) / np.hstack([a, b]) |
| 缺失处理 | np.nanmean(arr) |
| 保存/加载 | np.save / np.load / np.savetxt |
上一章:01 · Python 基础语法 | 下一章:03 · Pandas 数据处理与清洗