3.2.7 随机数与统计

- 掌握
numpy.random模块的常用函数 - 了解常用概率分布(均匀分布、正态分布、二项分布)
- 理解随机种子(seed)的作用
- 学会用 NumPy 进行基本统计运算
为什么需要随机数?
Section titled “为什么需要随机数?”在数据科学和 AI 中,随机数无处不在:
| 场景 | 为什么需要随机数 |
|---|---|
| 数据集拆分 | 随机拆分训练集和测试集 |
| 模型初始化 | 神经网络权重需要随机初始化 |
| 数据增强 | 随机裁剪、旋转、翻转图片 |
| 蒙特卡洛模拟 | 用随机采样估计复杂问题 |
| A/B 测试 | 随机分配用户到对照组和实验组 |
numpy.random 基础
Section titled “numpy.random 基础”新版 API(推荐)
Section titled “新版 API(推荐)”NumPy 推荐使用新版的 Generator API:
import numpy as np
# 创建随机数生成器rng = np.random.default_rng(seed=42)
# 均匀分布随机数 [0, 1)print(rng.random(5))# [0.773... 0.438... 0.858... 0.697... 0.094...]
# 指定范围的随机整数print(rng.integers(1, 100, size=5))# [67 82 42 91 23](示例值)
# 正态分布随机数print(rng.standard_normal(5))# [-0.15... 0.74... -0.27... ...]旧版 API(仍然常用)
Section titled “旧版 API(仍然常用)”很多教程和代码中还在用旧版 API,你也需要认识:
# 旧版写法(仍然有效)np.random.seed(42) # 设置全局种子
# 均匀随机数 [0, 1)print(np.random.rand(3))
# 标准正态分布print(np.random.randn(3))
# 随机整数print(np.random.randint(1, 100, size=5))随机种子:让”随机”可重复
Section titled “随机种子:让”随机”可重复”在科学研究和调试中,我们经常需要”可重复的随机”——每次运行代码得到相同的结果。
# 不设种子:每次结果不同print(np.random.rand(3)) # 每次都不一样
# 设置种子:每次结果相同np.random.seed(42)print(np.random.rand(3)) # [0.374... 0.950... 0.731...]
np.random.seed(42) # 重新设置相同的种子print(np.random.rand(3)) # [0.374... 0.950... 0.731...] 一模一样!# 新版 API 的种子设置rng = np.random.default_rng(seed=42)print(rng.random(3))
rng2 = np.random.default_rng(seed=42) # 相同的种子print(rng2.random(3)) # 相同的结果常用概率分布
Section titled “常用概率分布”每个值出现的概率相同:
rng = np.random.default_rng(42)
# [0, 1) 之间的均匀分布uniform_01 = rng.random(10000)print(f"均值: {uniform_01.mean():.4f}") # ≈ 0.5print(f"最小: {uniform_01.min():.4f}") # ≈ 0print(f"最大: {uniform_01.max():.4f}") # ≈ 1
# [low, high) 之间的均匀分布uniform_custom = rng.uniform(low=10, high=50, size=1000)print(f"均值: {uniform_custom.mean():.1f}") # ≈ 30正态分布(高斯分布)
Section titled “正态分布(高斯分布)”这是最重要的分布——在自然界和数据中无处不在:
rng = np.random.default_rng(42)
# 标准正态分布:均值=0, 标准差=1standard = rng.standard_normal(10000)print(f"均值: {standard.mean():.4f}") # ≈ 0print(f"标准差: {standard.std():.4f}") # ≈ 1
# 指定均值和标准差的正态分布# 例如:中国成年男性身高约 170cm,标准差约 6cmheights = rng.normal(loc=170, scale=6, size=10000)print(f"平均身高: {heights.mean():.1f} cm")print(f"标准差: {heights.std():.1f} cm")print(f"最矮: {heights.min():.1f} cm")print(f"最高: {heights.max():.1f} cm")n 次独立实验中成功的次数(比如掷硬币):
rng = np.random.default_rng(42)
# 模拟掷 10 次硬币(正面概率 0.5),重复 10000 次results = rng.binomial(n=10, p=0.5, size=10000)print(f"平均正面次数: {results.mean():.2f}") # ≈ 5print(f"最少: {results.min()}")print(f"最多: {results.max()}")其他常用分布
Section titled “其他常用分布”rng = np.random.default_rng(42)
# 泊松分布(事件发生次数)# 比如:平均每小时来 5 个客人visitors = rng.poisson(lam=5, size=1000)print(f"泊松分布 - 均值: {visitors.mean():.2f}")
# 指数分布(事件间隔时间)wait_times = rng.exponential(scale=2.0, size=1000)print(f"指数分布 - 均值: {wait_times.mean():.2f}")
# 选择:从数组中随机选取names = np.array(["Alice", "Bob", "Charlie", "Diana", "Eve"])chosen = rng.choice(names, size=3, replace=False) # 不放回抽样print(f"随机选取: {chosen}")rng = np.random.default_rng(42)
arr = np.arange(10) # [0 1 2 3 4 5 6 7 8 9]
# 打乱(原地修改)rng.shuffle(arr)print(arr) # [8 1 5 0 7 2 9 4 3 6](随机顺序)
# 打乱并返回新数组(不修改原数组)arr2 = np.arange(10)shuffled = rng.permutation(arr2)print(arr2) # [0 1 2 3 4 5 6 7 8 9] 原数组不变print(shuffled) # 打乱后的新数组rng = np.random.default_rng(42)
data = np.arange(100)
# 有放回抽样(可能重复)sample1 = rng.choice(data, size=10, replace=True)print(f"有放回: {sample1}")
# 无放回抽样(不重复)sample2 = rng.choice(data, size=10, replace=False)print(f"无放回: {sample2}")
# 加权随机抽样items = np.array(["常见", "一般", "稀有", "传说"])weights = np.array([0.6, 0.25, 0.1, 0.05]) # 概率drops = rng.choice(items, size=20, p=weights)unique, counts = np.unique(drops, return_counts=True)for item, count in zip(unique, counts): print(f" {item}: {count} 次")NumPy 提供了丰富的统计函数:
rng = np.random.default_rng(seed=42)data = rng.normal(loc=75, scale=10, size=100) # 100 个学生的成绩
print("=== 描述性统计 ===")print(f"均值 (mean): {np.mean(data):.2f}")print(f"中位数 (median): {np.median(data):.2f}")print(f"标准差 (std): {np.std(data):.2f}")print(f"方差 (var): {np.var(data):.2f}")print(f"最小值 (min): {np.min(data):.2f}")print(f"最大值 (max): {np.max(data):.2f}")print(f"极差 (ptp): {np.ptp(data):.2f}") # max - minrng = np.random.default_rng(seed=42)data = rng.normal(loc=75, scale=10, size=1000)
# 百分位数print(f"25th 百分位: {np.percentile(data, 25):.2f}")print(f"50th 百分位: {np.percentile(data, 50):.2f}") # = 中位数print(f"75th 百分位: {np.percentile(data, 75):.2f}")print(f"90th 百分位: {np.percentile(data, 90):.2f}")
# 四分位距 (IQR)q1 = np.percentile(data, 25)q3 = np.percentile(data, 75)iqr = q3 - q1print(f"四分位距 (IQR): {iqr:.2f}")rng = np.random.default_rng(seed=42)
# 身高和体重通常正相关height = rng.normal(170, 8, 100)weight = height * 0.6 - 30 + rng.normal(0, 5, 100) # 近似线性关系 + 噪声
# 计算相关系数矩阵corr_matrix = np.corrcoef(height, weight)print(f"相关系数: {corr_matrix[0, 1]:.4f}") # ≈ 0.7~0.9(正相关)
# 解读:# 1.0 = 完全正相关# 0.0 = 无关# -1.0 = 完全负相关rng = np.random.default_rng(seed=42)scores = rng.normal(75, 10, 200)
# 统计各分数段人数bins = [0, 60, 70, 80, 90, 100]counts, bin_edges = np.histogram(scores, bins=bins)labels = ["不及格", "及格", "中等", "良好", "优秀"]
print("=== 成绩分布 ===")for label, count, left, right in zip(labels, counts, bin_edges[:-1], bin_edges[1:]): bar = "█" * count print(f" {label} [{left:.0f}-{right:.0f}): {count:3d} {bar}")实战:模拟蒙特卡洛
Section titled “实战:模拟蒙特卡洛”蒙特卡洛方法是用随机数估计复杂问题的经典方法。下面用它来估计圆周率 π:
import numpy as np
def estimate_pi(n_points): """ 通过在正方形中随机撒点估计 π 落在四分之一圆内的点的比例 ≈ π/4 """ rng = np.random.default_rng(42)
# 在 [0, 1] × [0, 1] 的正方形中随机撒点 x = rng.random(n_points) y = rng.random(n_points)
# 计算到原点的距离 distance = np.sqrt(x**2 + y**2)
# 落在四分之一圆内(距离 <= 1)的点的数量 inside = np.sum(distance <= 1)
# π ≈ 4 × (圆内点数 / 总点数) pi_estimate = 4 * inside / n_points return pi_estimate
# 不同点数的估计精度for n in [100, 1000, 10000, 100000, 1000000]: pi_est = estimate_pi(n) error = abs(pi_est - np.pi) print(f" {n:>10,} 个点 → π ≈ {pi_est:.6f} 误差: {error:.6f}")输出摘要:
| 点数 | π 估计值 | 误差 |
|---|---|---|
| 100 | 3.120000 | 0.021593 |
| 1,000 | 3.156000 | 0.014407 |
| 10,000 | 3.153200 | 0.011607 |
| 100,000 | 3.140480 | 0.001113 |
| 1,000,000 | 3.142484 | 0.000891 |
点越多,估计越精确!这就是蒙特卡洛方法的魅力。
学完这一页,至少保留这张证据卡:
- 数组状态
- 操作前的形状、dtype、轴和样本值
- 操作
- 索引、切片、广播、reshape、线性代数,或随机/统计函数
- 输出
- 结果数组形状、值,或统计量
- 失败检查
- 轴混淆、视图/副本陷阱、广播不匹配或形状错误
- 期望产出
- 打印的形状和值,便于检查数组运算
root((随机数与统计)) 随机数生成 random / rand 均匀分布 normal / randn 正态分布 integers / randint 随机整数 choice 随机选取 shuffle / permutation 打乱 随机种子 seed 保证可重复 default_rng 新版 API 概率分布 均匀分布 uniform 正态分布 normal 二项分布 binomial 泊松分布 poisson 统计函数 mean / median / std / var min / max / argmin / argmax percentile 百分位 corrcoef 相关系数 histogram 直方图练习 1:模拟掷骰子
Section titled “练习 1:模拟掷骰子”rng = np.random.default_rng(42)
# 模拟掷 2 个骰子 10000 次# 1. 生成 10000×2 的随机整数数组(每行是一次投掷的两个骰子)# 2. 计算每次投掷的点数之和# 3. 统计每种点数和(2~12)出现的次数# 4. 找出出现最多的点数和(应该是 7)练习 2:模拟股票价格
Section titled “练习 2:模拟股票价格”rng = np.random.default_rng(42)
# 模拟一只股票 250 个交易日的价格变化# 初始价格 100 元# 每天的收益率服从正态分布:均值 0.05%,标准差 2%initial_price = 100n_days = 250
# 1. 生成 250 天的日收益率# daily_returns = rng.normal(loc=?, scale=?, size=?)
# 2. 计算每天的价格(提示:用 np.cumprod)# prices = initial_price * np.cumprod(1 + daily_returns)
# 3. 计算最终价格、最高价、最低价# 4. 计算年化收益率练习 3:批次指标分析
Section titled “练习 3:批次指标分析”rng = np.random.default_rng(seed=42)
# 生成 200 个推理批次的指标accuracy = rng.normal(0.78, 0.08, 200).clip(0, 1)latency_ms = rng.normal(180, 35, 200).clip(40, None)
# 1. 分别计算两个指标的均值、标准差、中位数# 2. 计算 accuracy 和 latency 的相关系数# 3. 统计 accuracy < 0.7 且 latency < 160 ms 的批次数# 4. 用 histogram 统计两个指标的分布# 5. 计算 accuracy Top 10 批次的平均 latency参考实现与讲解
- 骰子模拟可创建
(10000, 2)数组,沿axis=1求和,再用np.bincount统计点数和。最常见的和通常应是7,因为组合数量最多。 - 股票模拟先生成每日收益率,再用
100 * np.cumprod(1 + returns)得到价格路径。报告最终收益,如有要求再报告最大回撤,并配一张图让路径可见。 - 批次指标练习用
mean、std、corrcoef、布尔筛选、直方图和 top-k 排序完成。务必说明 random seed,方便别人复现实验样本。
章节总结:NumPy 知识全景
Section titled “章节总结:NumPy 知识全景”恭喜你完成了 NumPy 的全部内容!来回顾一下这一章学了什么:
flowchart TB subgraph "第1章 NumPy 科学计算" A["1.1 概述<br/>为什么用 NumPy"] --> B["1.2 数组基础<br/>创建 + 属性 + dtype"] B --> C["1.3 索引与切片<br/>基本/布尔/花式索引"] C --> D["1.4 数组运算<br/>向量化 + 广播 + 聚合"] D --> E["1.5 变形操作<br/>reshape + 拼接 + 分割"] E --> F["1.6 线性代数<br/>矩阵乘法 + 求逆 + 特征值"] F --> G["1.7 随机数与统计<br/>分布 + 采样 + 统计量"] end
G --> H["✅ 你已掌握 NumPy 核心!<br/>准备进入 Pandas →"]
style H fill:#4caf50,color:#fff✅ 自检: 你能用 NumPy 创建一个 100×3 的随机矩阵,计算每列的均值和标准差,并找出每行最大值所在的列索引吗?
import numpy as np
rng = np.random.default_rng(42)matrix = rng.normal(loc=50, scale=15, size=(100, 3))
# 每列均值print("每列均值:", np.mean(matrix, axis=0))
# 每列标准差print("每列标准差:", np.std(matrix, axis=0))
# 每行最大值所在的列索引print("每行最大值列索引:", np.argmax(matrix, axis=1))如果这些都不在话下——恭喜,准备好进入 Pandas 的世界了!