第一章:数据加载与初步探索
各位同学好,我是老蓝。今天咱们正式开始《量化因子计算数据预处理流程》的第一课。
做量化投资,说白了就是跟数据打交道。你策略再牛,因子再精巧,数据一塌糊涂,结果就是垃圾进垃圾出。我见过太多人,因子算出来回测曲线漂亮得不行,一上实盘就崩——查到最后,原来是原始数据加载的时候就出了问题。
所以,数据加载和初步探索,是整条流水线的第一道关卡。今天我就带大家把这关过扎实了。
1.1 数据从哪里来?——三种常见数据源
做量化,数据源无非三种:CSV文件、Excel表格、数据库。我个人的习惯是:
- CSV:最常用,适合日频数据、因子值存储,轻量级
- Excel:适合小规模数据、手工整理的数据,或者跟非技术人员对接
- 数据库:生产环境必备,高频数据、分钟级数据、实时数据,必须上库
咱们先看代码怎么读。
1.1.1 读取CSV
import pandas as pd
# 最基础的读法
df = pd.read_csv('stock_daily.csv')
# 我常用的读法——指定日期列,顺便把索引设好
df = pd.read_csv('stock_daily.csv', parse_dates=['trade_date'], index_col='trade_date')
# 如果文件很大,可以先瞄一眼
df_sample = pd.read_csv('stock_daily.csv', nrows=100)
我的小经验: 读CSV时,
parse_dates参数一定要用。很多新手读进来日期是字符串,后面处理各种报错。我早期踩过这个坑,后来就养成习惯了。
1.1.2 读取Excel
# 读Excel
df = pd.read_excel('factor_data.xlsx', sheet_name='Sheet1')
# 如果Excel里有多个sheet,可以这样
dfs = pd.read_excel('factor_data.xlsx', sheet_name=None)
# 返回的是一个字典,key是sheet名
注意: Excel文件有行数限制(最多1048576行),而且读起来比CSV慢很多。我建议:日常开发用Excel,生产环境转成CSV或数据库。
1.1.3 读取数据库
import pymysql
from sqlalchemy import create_engine
# 连接数据库
engine = create_engine('mysql+pymysql://user:password@host:port/dbname')
# 读取表
df = pd.read_sql('SELECT * FROM daily_data WHERE trade_date >= "2024-01-01"', con=engine)
# 或者读整个表
df = pd.read_sql_table('daily_data', con=engine)
数据库这块,我建议用SQLAlchemy,它帮你管理连接池,比裸用pymysql省心很多。
1.2 数据长什么样?——info()与describe()
数据加载进来之后,第一件事不是急着算因子,而是先看看数据长什么样。我一般两步走:
1.2.1 info()——看结构
df.info()
这个函数会告诉你:
- 总共有多少行、多少列
- 每列的数据类型(dtype)
- 每列有多少个非空值
- 内存占用情况
举个例子,输出可能是这样的:
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 100000 entries, 2024-01-02 to 2024-12-31
Data columns (total 8 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 stock_code 100000 non-null object
1 open 99850 non-null float64
2 high 99850 non-null float64
3 low 99850 non-null float64
4 close 99900 non-null float64
5 volume 100000 non-null int64
6 amount 100000 non-null float64
7 vwap 99500 non-null float64
dtypes: float64(6), int64(1), object(1)
memory usage: 7.6+ MB
关键看三点:
- 行数对不对?比如你预期有1000只股票×244个交易日,那应该是244000行
- 列类型对不对?比如日期应该是datetime,价格应该是float
- 空值多不多?如果某列空值超过10%,就要警惕了
1.2.2 describe()——看分布
df.describe()
这个函数给出数值型列的统计摘要:
| 统计量 | 含义 | 我关注什么 |
|---|---|---|
| count | 非空值数量 | 跟info()交叉验证 |
| mean | 均值 | 跟常识对比,比如股价均值在合理范围吗? |
| std | 标准差 | 波动率异常?可能数据有误 |
| min / max | 最小值 / 最大值 | 有没有极端值?比如股价为0或负数 |
| 25% / 50% / 75% | 四分位数 | 数据分布是否合理 |
避坑指南: 我曾经遇到一个因子数据,describe()显示最小值是-9999。查了半天,原来是数据源用-9999表示缺失值。这种「伪缺失值」在金融数据里很常见,一定要先处理掉。
1.3 数据形状与列名——心里要有数
看完了info()和describe(),接下来要确认两件事:数据有多大?列名叫什么?
1.3.1 查看数据形状
# 行数和列数
print(df.shape) # 输出 (100000, 8)
# 只看行数
print(len(df))
# 只看列数
print(len(df.columns))
shape返回的是一个元组,(行数, 列数)。我习惯先看一眼shape,心里有个底——比如100万行×50列,那后面处理就要注意内存了。
1.3.2 查看列名
# 所有列名
print(df.columns.tolist())
# 只看前几列
print(df.columns[:5])
# 重命名列
df.rename(columns={'close': 'close_price', 'volume': 'volume_share'}, inplace=True)
列名这块,我建议统一风格。比如全部小写、下划线分隔。不要有的叫"ClosePrice",有的叫"close_price",后面写因子公式时容易搞混。
1.4 本章知识体系
下面这张图,是我自己总结的数据加载与初步探索的完整流程,大家可以保存下来:
这张图把整个流程串起来了:从数据源出发,经过加载,进入初步探索,最后输出数据质量报告。每一步都有对应的pandas函数,大家照着做就行。
1.5 总结
今天的内容其实就三件事:
- 加载数据——CSV用read_csv,Excel用read_excel,数据库用read_sql
- 看结构——info()看行列、类型、空值;describe()看分布、极值
- 看形状——shape看大小,columns看列名,该改的改,该统一的统一
这三步做完,你对数据就有了一个基本的判断。哪些列有问题,哪些数据需要清洗,心里大概就有数了。
下一章,咱们开始真正动手清洗数据——处理缺失值、重复值、异常值。这些都是实战中天天要面对的问题。