NumPy入门:数组创建、数组运算、矩阵操作、随机数生成
做量化交易,说白了就是跟数据打交道。K线数据、因子数据、回测数据……每天面对的都是成千上万的数字。Python自带的列表处理这些数据?嗯,慢得让人抓狂。这时候NumPy就派上用场了。
我个人习惯把NumPy叫做「量化交易的基石」。没有它,后面学Pandas、回测框架都会很吃力。今天我们就把它啃下来。
一、NumPy数组的创建
先说说最基础的东西——怎么创建一个数组。别小看这一步,我在项目中遇到过不少新手因为数组类型搞错,导致后面计算全崩了。
1. 从列表创建
最简单的方式,直接把Python列表传进去:
import numpy as np
# 一维数组
arr1 = np.array([1, 2, 3, 4, 5])
print(arr1) # [1 2 3 4 5]
# 二维数组(矩阵)
arr2 = np.array([[1, 2, 3], [4, 5, 6]])
print(arr2)
# [[1 2 3]
# [4 5 6]]
小提示:创建数组时最好指定dtype,比如
np.array([1,2,3], dtype=np.float64)。我曾经因为默认整数类型导致除法结果被截断,查了半天bug。
2. 常用创建函数
实际工作中,我们很少手动敲数据。更多是用这些函数:
| 函数 | 说明 | 示例 |
|---|---|---|
np.zeros() |
创建全0数组 | np.zeros((3,4)) |
np.ones() |
创建全1数组 | np.ones((2,3)) |
np.arange() |
类似range,返回数组 | np.arange(0, 10, 2) |
np.linspace() |
等间隔取数 | np.linspace(0, 1, 5) |
np.eye() |
单位矩阵 | np.eye(3) |
# 实战中常用的写法
zeros_arr = np.zeros((10000, 5)) # 1万行5列,初始化因子矩阵
arange_arr = np.arange(100) # 0到99,做索引用
linspace_arr = np.linspace(0, 1, 50) # 0到1之间均匀取50个点
二、数组运算
NumPy最爽的地方就是向量化运算。不用写循环,一个符号搞定所有元素。
1. 基本算术运算
数组之间可以直接加减乘除,对应位置元素各自计算:
a = np.array([10, 20, 30, 40])
b = np.array([1, 2, 3, 4])
print(a + b) # [11 22 33 44]
print(a - b) # [9 18 27 36]
print(a * b) # [10 40 90 160]
print(a / b) # [10. 10. 10. 10.]
重点:向量化运算比Python循环快几十倍。我回测时处理百万级数据,用循环要等几分钟,换成向量化运算秒出结果。
2. 广播机制
不同形状的数组也能运算?没错,这就是广播。说白了,NumPy会自动把小的数组「扩展」成大的形状。
# 标量广播
arr = np.array([[1, 2, 3], [4, 5, 6]])
print(arr + 100)
# [[101 102 103]
# [104 105 106]]
# 一维数组广播到二维
row = np.array([10, 20, 30])
print(arr + row)
# [[11 22 33]
# [14 25 36]]
注意:广播不是万能的。形状不匹配时会报错。比如(3,4)的数组和(3,5)的数组没法广播。我曾经在这上面栽过跟头,写代码前最好先检查一下shape。
3. 统计运算
量化分析离不开统计指标。NumPy提供了全套工具:
data = np.array([1.2, 2.5, 3.8, 4.1, 5.6])
print(np.mean(data)) # 均值:3.44
print(np.std(data)) # 标准差:1.58
print(np.max(data)) # 最大值:5.6
print(np.min(data)) # 最小值:1.2
print(np.percentile(data, 25)) # 25分位数:2.5
三、矩阵操作
量化交易里,矩阵操作太常见了。协方差矩阵、相关系数矩阵、投资组合优化……都离不开它。
1. 矩阵乘法
注意区分 * 和 @:
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
# 对应元素相乘(Hadamard积)
print(A * B)
# [[ 5 12]
# [21 32]]
# 真正的矩阵乘法
print(A @ B)
# [[19 22]
# [43 50]]
2. 转置与重塑
这两个操作我几乎每天都要用:
arr = np.array([[1, 2, 3], [4, 5, 6]])
# 转置
print(arr.T)
# [[1 4]
# [2 5]
# [3 6]]
# 重塑形状
print(arr.reshape(3, 2))
# [[1 2]
# [3 4]
# [5 6]]
# 展平成一维
print(arr.flatten())
# [1 2 3 4 5 6]
3. 矩阵求逆与行列式
做线性回归或优化问题时,经常需要求逆矩阵:
from numpy.linalg import inv, det
M = np.array([[1, 2], [3, 4]])
M_inv = inv(M) # 求逆
print(M_inv)
# [[-2. 1. ]
# [ 1.5 -0.5]]
M_det = det(M) # 行列式
print(M_det) # -2.0
避坑指南:我曾经在计算投资组合权重时,忘了检查矩阵是否可逆。结果程序跑着跑着就报LinAlgError。现在我的习惯是:求逆之前先算行列式,如果接近0就改用伪逆
np.linalg.pinv()。
四、随机数生成
随机数在量化里用途很广:蒙特卡洛模拟、随机游走、回测中的随机抽样……
1. 基本随机数
# 设置种子,保证结果可复现
np.random.seed(42)
# 均匀分布 [0,1)
rand_arr = np.random.rand(3, 4)
print(rand_arr)
# 正态分布
norm_arr = np.random.randn(1000) # 均值0,标准差1
# 指定范围整数
int_arr = np.random.randint(0, 100, size=10) # 0到99的10个整数
2. 高级随机分布
做风险分析时,我经常用到这些:
# 二项分布(模拟涨跌次数)
binom_arr = np.random.binomial(n=100, p=0.5, size=10)
# 泊松分布(模拟交易次数)
poisson_arr = np.random.poisson(lam=5, size=20)
# 随机抽样
population = np.array(['A', 'B', 'C', 'D', 'E'])
sample = np.random.choice(population, size=3, replace=False)
3. 随机打乱
做交叉验证或数据洗牌时很有用:
data = np.array([[1, 2], [3, 4], [5, 6], [7, 8]])
np.random.shuffle(data) # 原地打乱行顺序
print(data)
# 每次运行结果不同
五、知识体系总览
下面这张图是我自己总结的NumPy核心脉络,建议保存下来对照学习:
嗯,到这里NumPy的核心内容就差不多了。你想想看,数组创建、运算、矩阵操作、随机数,这四个模块基本覆盖了量化交易中90%的数据处理场景。我个人建议你把这些代码都敲一遍,光看是记不住的。
下次遇到数据处理的活儿,先想想能不能用NumPy的向量化操作代替循环。养成这个习惯,你的代码会又快又优雅。