NumPy进阶:随机数生成、线性代数运算、统计函数、文件读写
说实话,NumPy的基础操作大家可能都熟悉,但真正到了量化实战中,你会发现那些看似简单的功能背后藏着不少门道。今天我就把工作中最常用的几个进阶模块掰开揉碎讲清楚。
一、随机数生成:不只是rand()那么简单
做量化的人,天天跟随机数打交道。蒙特卡洛模拟、回测中的随机抽样、因子有效性检验……哪个都离不开它。我刚开始用NumPy时,总觉得随机数生成不就是np.random.rand()嘛,后来踩了坑才知道这里水挺深。
1. 随机种子:让结果可复现
你想想看,做研究最怕什么?结果不可复现。今天跑出来的结果,明天再跑就不一样了,那还怎么调试?
import numpy as np
# 设置随机种子
np.random.seed(42)
# 生成均匀分布随机数
data = np.random.rand(3, 4)
print(data)
我的习惯:每个项目开始前,我都会在配置文件里固定一个种子值。这样团队其他人复现结果时,只要用同一个种子就行。我曾经因为没设种子,被同事追着问“你那个结果我怎么跑不出来”……尴尬。
2. 常用分布函数
量化里常用的分布,NumPy基本都支持。我列几个最常用的:
| 函数 | 分布类型 | 量化应用场景 |
|---|---|---|
np.random.normal() |
正态分布 | 收益率模拟、风险建模 |
np.random.uniform() |
均匀分布 | 参数随机搜索、初始化权重 |
np.random.binomial() |
二项分布 | 交易胜率模拟 |
np.random.exponential() |
指数分布 | 事件间隔时间建模 |
# 生成10000个正态分布随机数,模拟收益率
returns = np.random.normal(loc=0.001, scale=0.02, size=10000)
# 生成二项分布,模拟10次交易中成功次数
trades = np.random.binomial(n=10, p=0.6, size=1000)
注意:别用
np.random.seed()在循环里反复设置。我见过有人每生成一个随机数就设一次种子,结果所有“随机数”都一样……那还叫随机吗?
二、线性代数运算:矩阵运算的利器
做因子分析,说白了就是跟矩阵打交道。协方差矩阵、相关系数矩阵、特征分解……这些在NumPy里都有现成的实现。
1. 矩阵乘法
别用*做矩阵乘法!这是初学者最容易犯的错误。*是逐元素相乘,矩阵乘法得用@或np.dot()。
# 创建两个矩阵
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
# 矩阵乘法
C = A @ B # 或者 np.dot(A, B)
print(C)
2. 特征值与特征向量
主成分分析(PCA)里,特征分解是核心步骤。我当年做多因子模型时,就是用这个来降维的。
# 计算特征值和特征向量
eigenvalues, eigenvectors = np.linalg.eig(cov_matrix)
# 按特征值大小排序
idx = np.argsort(eigenvalues)[::-1]
eigenvalues = eigenvalues[idx]
eigenvectors = eigenvectors[:, idx]
3. 矩阵求逆与解线性方程组
# 矩阵求逆
inv_A = np.linalg.inv(A)
# 解线性方程组 Ax = b
x = np.linalg.solve(A, b)
避坑指南:我曾经在计算投资组合权重时,直接用
np.linalg.inv()求逆,结果矩阵接近奇异,算出来的权重离谱得很。后来改用np.linalg.solve(),不仅更稳定,速度还更快。
三、统计函数:快速描述数据特征
拿到一批因子数据,第一件事就是看它的统计特征。NumPy的统计函数虽然基础,但组合起来用很强大。
1. 描述性统计
data = np.random.normal(loc=0, scale=1, size=1000)
# 基本统计量
mean = np.mean(data) # 均值
std = np.std(data) # 标准差
var = np.var(data) # 方差
min_val = np.min(data) # 最小值
max_val = np.max(data) # 最大值
median = np.median(data) # 中位数
p25 = np.percentile(data, 25) # 25分位数
p75 = np.percentile(data, 75) # 75分位数
2. 相关性分析
因子之间有没有多重共线性?因子和收益率有没有相关性?这些都得算。
# 计算相关系数矩阵
corr_matrix = np.corrcoef(factor_data, rowvar=False)
# 计算协方差矩阵
cov_matrix = np.cov(factor_data, rowvar=False)
我的经验:算相关系数时,记得先检查数据有没有缺失值。我遇到过因为一个NaN导致整个相关系数矩阵全是NaN的情况。用
np.isnan()先检查一下,或者用np.nanmean()这类函数跳过缺失值。
四、文件读写:数据持久化
做量化,数据就是命根子。怎么高效地读写数据,是每个量化工程师的必修课。
1. CSV文件读写
# 保存数据到CSV
np.savetxt('factor_data.csv', factor_array, delimiter=',',
header='factor1,factor2,factor3', comments='')
# 读取CSV数据
data = np.loadtxt('factor_data.csv', delimiter=',', skiprows=1)
2. NumPy原生格式
如果只是临时保存中间结果,用.npy格式更快,而且能保留数据类型。
# 保存为npy格式
np.save('factor_data.npy', factor_array)
# 读取npy文件
data = np.load('factor_data.npy')
3. 压缩存储
处理海量因子数据时,文件大小是个问题。用np.savez_compressed()可以压缩存储多个数组。
# 保存多个数组到压缩文件
np.savez_compressed('factors.npz',
factor1=factor1,
factor2=factor2,
returns=returns)
# 读取
data = np.load('factors.npz')
factor1 = data['factor1']
factor2 = data['factor2']
注意:用
np.loadtxt()读大文件时,内存占用会很高。我处理过上千万行的因子数据,直接读内存爆了。后来改用np.fromfile()配合二进制格式,速度快了10倍不止。
知识体系总览
下面这张图是我自己总结的NumPy进阶知识结构,方便你对照学习:
说实话,NumPy的这些进阶功能,刚开始可能觉得用不上。但等你真正开始做因子挖掘、回测系统时,就会发现它们无处不在。我的建议是:别急着背函数,先理解每个模块解决什么问题。用到的时候,自然就记住了。