02 · NumPy 数值计算基础

数据科学⭐⭐⭐⭐⭐核心必修|约 8 分钟13 节1 图
本章目标

掌握 NumPy 数组的创建、索引、运算、变形与随机数生成。网络药理学中的表达矩阵、成分-靶点矩阵本质上都是 NumPy 数组,务必熟练掌握。

本章路线
02 · NumPy 数值计算基础

0. 小白导读:NumPy 是什么?为什么需要它?

🍚生活化比喻

先听懂:想象你有一堆数据要算——比如 100 个样本 × 5000 个基因的表达量。用普通 Python 列表(list)算,就像用勺子挖西瓜,慢、费劲、还不顺手。而 NumPy 就像一把西瓜刀——一刀下去搞定一整片,又快又整齐。

专业说法:NumPy(Numerical Python 的简称)是 Python 做科学计算的"发动机"。它提供多维数组(ndarray),让计算从"逐个元素跑"变成"整批一起算",速度提升几十倍到上百倍。


1. 为什么需要 NumPy

Python 自带的列表(list)也能存数字,但有两个致命问题:

  1. :列表存储的是 Python 对象,计算时要逐个解释,速度远慢于 C 语言。
  2. 不能做真正的数学运算[1,2,3] * 2 得到 [1,2,3,1,2,3](重复),而不是 [2,4,6]

NumPy 用 C 语言实现底层,提供多维数组(ndarray),支持:

  • 向量化运算(整个数组一次算完,快几十倍)
  • 广播机制(不同形状的数组也能运算)
  • 丰富的数学、统计、线性代数函数
💡提示

一句话:NumPy 数组 = 高性能的"矩阵",是 Pandas 和 scikit-learn 的地基。

python
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. 数组创建

🍚生活化比喻

先听懂:NumPy 数组就像一块块整齐的格子板

  • 一维数组 = 一行格子(像一排储物柜)

  • 二维数组 = 一张表格(像 Excel 表,有行有列)

  • 三维数组 = 一摞表格(像一叠叠好的报表)

创建数组,就是"把格子板搭好,往格子里填数"。

2.1 从列表创建

python
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 常用创建函数

python
# 全零 / 全一 / 单位矩阵
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. 数组属性

python
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 一维数组

python
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 二维数组

python
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 布尔索引(科研高频,重点)

python
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 花式索引(整数数组索引)

python
a = np.array([10, 20, 30, 40, 50])
idx = [0, 2, 4]
print(a[idx])        # [10 30 50](按索引列表取值)

5. 数组运算与广播机制

NumPy 数组与广播机制
图 02-1NumPy 数组与广播机制

5.1 逐元素运算

python
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 矩阵乘法(重点区分)

python
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 广播机制(重点)

🍚生活化比喻

先听懂:广播就像给不同尺寸的格子板"自动对齐"。比如一张 2×3 的表格要加一行 3 个数字,NumPy 会自动把这行"复制成 2 行",再逐格相加——你不用手动复制,它帮你做了。

广播规则:从最后一个维度开始对齐,维度相等或其中一个为 1 就可以运算

python
# 标量与数组
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]]
💡提示

科研场景:对表达矩阵做标准化(每列减去均值、除以标准差)就是广播的典型应用:

python
> 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. 常用统计函数

python
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 变形

python
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 拼接

python
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 随机数生成

python
# 固定形状随机数组
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) 之后,计算机产生的"随机数"其实是一套固定的序列——就像同一部电影每次播放都一样。这样别人(或未来的你)运行同一段代码,能得到一模一样的结果,实验才能被复现。

python
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

python
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 唯一值与排序

python
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 缺失值处理

python
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

python
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 文件保存与加载

python
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 个基因,标准正态分布),完成:

  1. 设置随机种子 np.random.seed(42),生成 np.random.randn(10, 5)
  2. 打印矩阵的 shapedtypesize
  3. 计算每个基因(每列)的均值、标准差。
  4. 筛选出所有基因均值 > 0 的样本行(布尔索引)。
  5. 把矩阵标准化(每列减均值除标准差,用广播)。
  6. 保存标准化矩阵为 normalized_matrix.csv

进阶练习

  1. np.linspace(0, 100, 21) 生成 21 个浓度点,计算 np.exp(-0.05 * x) 模拟释放曲线数据。
  2. np.random.choice 从 20 个基因中不放回抽 8 个,模拟随机抽样。
  3. np.linalg 计算一个 3×3 矩阵的特征值,验证特征值之和等于矩阵的迹(对角线之和)。
  4. 模拟 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 数据处理与清洗

配套学习资源

共 5 条 · 点击跳转