排序、随机数与模拟
前置知识:缺失值、统计与数值稳定性
本章目录
本章学习目标
- 区分排序后的值与原位置索引
- 使用独立 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。
记录尽量保存在当前浏览器,建议定期导出备份。