NumPy入门:从零搭建你的量化计算引擎

说实话,做量化交易这些年,我见过太多人一上来就扑到pandas上。结果呢?数据量一大,程序直接卡死。为什么?因为底层没打好。NumPy就是那个底层——它是Python量化世界的基石。

我个人习惯把NumPy比作「瑞士军刀」。它不花哨,但每个量化交易员都离不开。今天我们就从零开始,把这把刀磨锋利。

1. NumPy数组创建:从列表到数组的蜕变

先说说最基础的东西——怎么创建一个NumPy数组。你可能会想:「不就是把列表转一下吗?」嗯,还真没那么简单。

import numpy as np

# 从列表创建
arr1 = np.array([1, 2, 3, 4, 5])
print(arr1)  # [1 2 3 4 5]

# 创建全零数组
zeros = np.zeros((3, 4))
print(zeros)

# 创建全一数组
ones = np.ones((2, 3))

# 创建等差数列
seq = np.arange(0, 10, 2)  # [0 2 4 6 8]

# 创建等间隔数组
lin = np.linspace(0, 1, 5)  # [0.   0.25 0.5  0.75 1.  ]

我在项目中遇到过一个问题:用np.array()创建二维数组时,如果内层列表长度不一致,它会变成一维的对象数组。这坑我踩过,当时查了半天才发现是数据源有问题。

注意:创建数组时,数据类型会自动推断。但如果你需要精确控制,记得用 dtype 参数指定。比如 np.array([1,2,3], dtype=np.float64)

2. 数组属性:摸清你的数据底细

拿到一个数组,第一件事是什么?我个人习惯先看它的「三围」——形状、维度、大小。

arr = np.array([[1, 2, 3], [4, 5, 6]])

print(arr.shape)   # (2, 3)  —— 2行3列
print(arr.ndim)    # 2       —— 二维数组
print(arr.size)    # 6       —— 总共6个元素
print(arr.dtype)   # int64   —— 数据类型
print(arr.itemsize) # 8      —— 每个元素占8字节

你想想看,在量化交易中,我们经常要处理几千只股票的历史数据。如果不知道数组的形状,后续的切片、运算全都会乱套。我记得有一次回测结果对不上,最后发现是数组维度搞反了——行和列颠倒了。

属性 含义 量化场景举例
shape 数组形状 (1000, 5) 表示1000个交易日,5个字段
ndim 维度数 2表示二维表,3表示三维张量
dtype 数据类型 float64适合价格数据,int64适合数量

3. 数组索引与切片:精准定位你的数据

索引和切片,说白了就是「怎么从数组里把想要的数据掏出来」。这和Python列表很像,但NumPy更强大。

arr = np.array([[10, 20, 30], [40, 50, 60], [70, 80, 90]])

# 基本索引
print(arr[0, 1])    # 20 —— 第0行第1列
print(arr[2])       # [70 80 90] —— 第2行

# 切片
print(arr[:, 1])    # [20 50 80] —— 所有行的第1列
print(arr[0:2, :])  # 前两行所有列

# 花式索引
indices = [0, 2]
print(arr[indices])  # 第0行和第2行

# 布尔索引
mask = arr > 50
print(arr[mask])     # [60 70 80 90]

这里有个技巧:布尔索引在量化中特别常用。比如筛选出涨幅超过5%的股票,或者找出成交量异常的日期。我曾经用布尔索引写过一个选股策略,一行代码就搞定了复杂的条件筛选。

小提示:切片返回的是视图,不是副本。修改切片会影响原数组。如果你不想影响原数组,用 .copy() 方法。

4. 数组运算:量化计算的加速器

为什么NumPy快?因为它把循环放到了C语言层面执行。你写一个加法,它背后是向量化操作,比Python的for循环快几十倍。

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

# 算术运算
print(a + b)   # [5 7 9]
print(a * b)   # [4 10 18]
print(a ** 2)  # [1 4 9]

# 比较运算
print(a > 2)   # [False False  True]

# 矩阵运算
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
print(A @ B)   # 矩阵乘法
print(A * B)   # 逐元素乘法

你想想看,在量化交易中,计算收益率、波动率、协方差矩阵,全都是数组运算。我刚开始做策略回测时,用for循环算夏普比率,跑一次要5分钟。后来改成向量化运算,3秒搞定。这就是NumPy的魅力。

5. 通用函数:数学工具箱

通用函数,简称ufunc,是NumPy对数组进行逐元素运算的函数。说白了,就是不用写循环,直接对整个数组做数学操作。

prices = np.array([100.5, 101.2, 99.8, 102.0, 100.1])

# 常用数学函数
print(np.sqrt(prices))      # 平方根
print(np.log(prices))       # 自然对数
print(np.exp(prices/100))   # 指数
print(np.abs(prices - 100)) # 绝对值

# 统计函数
print(np.mean(prices))      # 均值
print(np.std(prices))       # 标准差
print(np.max(prices))       # 最大值
print(np.min(prices))       # 最小值

# 累积运算
print(np.cumsum(prices))    # 累积和
print(np.diff(prices))      # 差分 —— 计算每日涨跌

我记得有一次做因子分析,需要计算所有股票的日收益率。用np.diff()配合np.log(),一行代码就得到了对数收益率序列。如果用手写循环,代码量至少翻5倍。

核心要点:通用函数是向量化操作的核心。它能让你用数学公式的思维写代码,而不是用循环的思维。这是从「程序员」到「量化工程师」的关键一步。

本章知识体系

下面这张图,是我梳理的NumPy入门知识结构。你可以把它当作学习地图,随时回来对照。

NumPy入门 数组创建 np.array() np.zeros() / np.ones() np.arange() / np.linspace() 数组属性 shape / ndim / size dtype / itemsize 索引与切片 基本索引 [i, j] 切片 [start:stop] 花式索引 / 布尔索引 数组运算 算术运算 + - * / 比较运算 > < == 矩阵运算 @ 通用函数 数学函数 sqrt/log/exp 统计函数 mean/std/max 累积运算 cumsum/diff 核心思想:向量化操作,告别显式循环

NumPy的内容其实就这么多。但别小看它,我见过太多人因为基础不牢,后面写策略时各种报错。花一天时间把NumPy吃透,后面能省你一个月的时间。

嗯,今天就到这里。记住:量化交易的核心是数据处理,而NumPy就是那个让你处理数据快如闪电的工具。多练、多用、多踩坑,自然就熟了。


公众号:蓝海数据掘金营,微信deep3321