跳转到内容

排序、随机数与模拟

计算机基础 · NumPy

前置知识:缺失值、统计与数值稳定性

本章目录

本章学习目标

  • 区分排序后的值与原位置索引
  • 使用独立 Generator 复现随机实验
  • 解释 Monte Carlo 误差和重采样边界

一、排序与位置索引

np.sort 返回排序后的值,np.argsort 返回按排序顺序排列的原位置。例如收益 [0.02,-0.01,0.02] 的升序值是 [-0.01,0.02,0.02],位置可用稳定排序得到 [1,0,2]。当还有股票代码、行业等关联数组时,应先求 argsort,再用同一位置索引选择所有数组,不能只排序收益列而把标签留在原位。

并列值的顺序需要规则:稳定排序保留原出现顺序,但如果业务要求按代码打破并列,应明确二级键。np.lexsort 可以按多个键排序,最后一个键是主要键,容易记反。二维数组默认沿最后一个轴排序,不是对整个矩阵的元素统一排序。执行任何排序前都先问排序单位是一行记录、一列特征还是所有数值,并写出所需输出形状。

二、局部随机生成器与复现

推荐 rng=np.random.default_rng(seed) 创建 Generator,而不是在全局随意调用随机函数。种子确定一个随机流的起点,但同一个 rng 连续两次调用会推进状态,输出不会相同;重新创建同种子的生成器,按相同顺序调用才会重复序列。把 rng 显式传入模拟函数能减少不同代码片段互相影响。

复现还需要记录 NumPy 版本、BitGenerator、调用顺序、样本大小与分布参数,不能只写 seed=42 就保证任何版本任何算法位级一致。课程以锁定环境的实测结果为准。多个独立实验可以用不同流或 SeedSequence 管理子流,避免简单复制同一状态导致样本高度相关。随机种子用于复现,不会提升模型泛化能力,也不证明统计结论正确。

三、模拟的单位与统计误差

Monte Carlo 通过随机抽样近似期望或概率。模拟一万次抛硬币,先规定每次独立且正面概率为二分之一,再把正面次数除以总次数。结果不必精确等于二分之一,因为有限样本有随机误差;样本量增大时标准误通常按根号 n 的倒数缩小,不是增加十倍样本就增加十倍精度。

若模拟用户转化,抽样单位必须是用户,而不是把同一用户的多个事件当作独立样本。带回抽样的 bootstrap 可以估计统计量波动,但对时间序列、分组相关数据不能无条件逐行重采样,否则会破坏相关结构。量化示例中正态独立收益只是教学假设,不能把它当作真实市场模型或风险保证。报告必须包含假设、重复次数和不确定性来源。

四、choice、permutation 与抽样区别

permutation 返回随机排列,适合打乱行号或划分样本;choice 可以带回或不带回抽取,默认带回意味着同一个样本可能出现多次。不带回抽样数量不能超过总体大小。随机打乱时间序列可能将未来信息混入训练集,因此预测按时间切分时不能只因为代码方便就 permutation 全部行。

用 choice 对 [10,20,30] 带回抽五个值,每个位置都独立抽样,重复是正常现象;无放回取两个值则保证不同位置,但值本身若原总体已有重复仍可相同。对样本划分用唯一行号,检查训练、验证和测试集合无交集且覆盖全部样本;类别不平衡时可能还需要分层策略,不是所有随机划分都能稳定代表整体。

五、验证随机代码的性质

配套代码对 argsort 的顺序作精确断言,对同种子生成器输出作一致性断言,对模拟只检查取值范围和两种计算方式一致。不要要求一万次硬币一定有五千个正面,那是把统计期望误作每次必须满足的事实。实验报告打印实测估计和理论标准误,鼓励多换种子观察波动。

排序与随机一起用于数据处理时,先处理主键和时间,再决定是否允许打乱。验证边界包括空输入、单元素、并列键、超过总体大小的无放回请求。随机模拟可帮助理解概率,却不能替代数据收集设计和因果假设。在面试中能解释抽样单位、带回条件与复现限制,比记住随机函数名称更能体现对分析可靠性的理解。

完整示例与实际输出

import numpy as np
values=np.array([.02,-.01,.02]); labels=np.array(['A','B','C'])
order=np.argsort(values,kind='stable')
assert order.tolist()==[1,0,2]
print('ranked pairs',list(zip(labels[order].tolist(),values[order].tolist())))
r1=np.random.default_rng(2026); r2=np.random.default_rng(2026)
draws1=r1.normal(size=5); draws2=r2.normal(size=5)
assert np.array_equal(draws1,draws2)
print('repeatable normal',np.round(draws1,6).tolist())
rng=np.random.default_rng(2026); n=10000
heads=rng.integers(0,2,size=n)
estimate=heads.mean(); se=np.sqrt(.5*.5/n)
assert 0<=estimate<=1 and np.isclose(estimate,heads.sum()/n)
print('coin estimate/theoretical SE',float(estimate),float(se))
idx=rng.permutation(12); train=idx[:8]; test=idx[8:]
assert len(set(train)&set(test))==0 and len(set(idx))==12
print('split sizes',len(train),len(test))

实际运行通过CPU Python execution.

ranked pairs [('B', -0.01), ('A', 0.02), ('C', 0.02)]
repeatable normal [-0.793122, 0.240571, -1.896326, 1.395772, 0.638295]
coin estimate/theoretical SE 0.5003 0.005
split sizes 8 4

五道自测:先回答,再核对

01 · sort 和 argsort 的输出区别?

查看提示

值还是位置?

查看参考答案

sort 返回排序值,argsort 返回用于排序的原位置索引。

02 · 同一 rng 连续调用两次为何不同?

查看提示

内部状态推进。

查看参考答案

每次抽样都会推进随机流;需要重新创建相同状态才重复。

03 · choice(replace=False) 能从三行取五行吗?

查看提示

不放回。

查看参考答案

不能,样本数不能超过总体数。

04 · 一万次公平硬币是否必须五千次正面?

查看提示

期望与实现区分。

查看参考答案

不必;五千是期望,实际有随机波动。

05 · 时间序列预测为何不能任意随机打乱?

查看提示

训练时能否看到未来?

查看参考答案

会破坏时间顺序,可能把未来分布或信息带入训练。

两项动手任务

任务 1 · 按分数降序,分数并列按代码升序排列 [('B',3),('A',3),('C',1)]。

提示

lexsort 最后一个键为主键。

完整参考解答与检查方式

结果 A、B、C;负分数实现降序,代码实现并列规则。

import numpy as np
codes=np.array(['B','A','C']); scores=np.array([3,3,1])
idx=np.lexsort((codes,-scores))
assert codes[idx].tolist()==['A','B','C']
print(list(zip(codes[idx].tolist(),scores[idx].tolist())))

任务 2 · 对 [1,2,3,4] 用固定种子做500次带回 bootstrap,输出均值分布均值与标准差。

提示

每次抽四个样本;不能把 std 当绝对误差上限。

完整参考解答与检查方式

固定环境可复现,检查统计量处于原值范围,报告经验波动而非保证。

import numpy as np
data=np.array([1.,2.,3.,4.]); rng=np.random.default_rng(7)
draws=rng.choice(data,size=(500,4),replace=True)
means=draws.mean(axis=1)
assert means.shape==(500,) and np.all((means>=1)&(means<=4))
print(float(means.mean()),float(means.std(ddof=1)))

官方资料与掌握状态

完成两项任务后,按独立完成程度记录。状态会尽量保存在当前浏览器,可导出备份。

动手任务记录

掌握状态与复习

关掉资料独立完成后记 A;提示后完成记 B;需要重学记 C。

记录尽量保存在当前浏览器,建议定期导出备份。