第1章:数据读取与初步探索
做量化交易的朋友都知道,Tick数据是市场最原始的脉搏。每一笔成交、每一次报价,都藏着市场的真实意图。但说实话,拿到原始Tick数据的第一反应往往是——这玩意儿怎么这么乱?
别急,咱们一步步来。今天就从最基础的读取和探索开始。
1.1 用Pandas读取Tick数据
我个人习惯用Pandas来处理Tick数据。它读取CSV文件的速度快,而且后续清洗、分析都很顺手。先看个最简单的例子:
import pandas as pd
# 读取Tick数据
df = pd.read_csv('tick_data.csv')
print('数据加载完成')
嗯,这里要注意几个坑。我在项目中遇到过,有些Tick数据文件特别大,动不动就几个G。直接读内存会爆。我建议加上参数优化:
# 高效读取大文件
df = pd.read_csv(
'tick_data.csv',
dtype={'symbol': 'category', 'price': 'float32', 'volume': 'int32'},
parse_dates=['timestamp'],
low_memory=False
)
为什么要指定dtype?你想想看,默认情况下Pandas会用float64存价格,但Tick数据的价格精度通常不需要那么高。用float32能省一半内存。symbol用category类型,对于几千只股票来说,内存占用能减少90%以上。
我曾经一次性读取了全市场某天的Tick数据,没做任何优化,结果16G内存直接爆掉。后来老老实实按天分文件,加上dtype优化,才搞定。
1.2 快速预览数据
数据读进来了,第一件事就是看看长什么样。head()和tail()是我最常用的两个函数:
# 查看前5行
print(df.head())
# 查看后5行
print(df.tail())
# 查看前10行
print(df.head(10))
为什么还要看tail?因为有些数据源会在文件末尾追加脏数据。我遇到过好几次,最后几行是测试数据或者空行。不看tail的话,后面分析全白做。
来看个实际输出例子:
timestamp symbol price volume direction
0 2024-01-02 09:30:00.123 000001 10.25 100 1
1 2024-01-02 09:30:00.456 000001 10.26 200 1
2 2024-01-02 09:30:00.789 000001 10.25 150 -1
3 2024-01-02 09:30:01.012 000001 10.27 300 1
4 2024-01-02 09:30:01.345 000001 10.26 250 -1
看到没?每一行就是一笔成交。timestamp精确到毫秒,price是成交价,volume是成交量,direction表示买卖方向(1买,-1卖)。
1.3 数据概览:info()和describe()
光看几行数据不够,得对整个数据集有个全局认识。info()能告诉你每列的类型、非空数量、内存占用:
print(df.info())
输出类似这样:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1000000 entries, 0 to 999999
Data columns (total 5 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 timestamp 1000000 non-null datetime64[ns]
1 symbol 1000000 non-null category
2 price 1000000 non-null float32
3 volume 1000000 non-null int32
4 direction 1000000 non-null int8
dtypes: category(1), datetime64[ns](1), float32(1), int32(1), int8(1)
memory usage: 15.3 MB
这里有个细节:Non-Null Count。如果某列的非空数量小于总行数,说明有缺失值。我见过最离谱的一次,某只股票的Tick数据有30%的price是空的——那天的行情系统出了故障。
再看describe(),它能给出数值列的统计信息:
print(df.describe())
price volume direction
count 1000000.00 1000000.000 1000000.000
mean 10.35 200.50 0.120
std 0.15 150.30 0.980
min 9.80 1.00 -1.000
25% 10.25 100.00 -1.000
50% 10.35 200.00 1.000
75% 10.45 300.00 1.000
max 10.80 5000.00 1.000
你看,min和max能帮你快速发现异常值。比如某只股票正常价格在10块左右,突然出现一个9.8或者10.8,那就要警惕了——可能是数据错误,也可能是极端行情。
对于Tick数据,我习惯先看count和min/max。如果count远小于总行数,或者min/max明显偏离正常范围,那这数据八成有问题,得先清洗再往下走。
1.4 检查数据形状
shape属性告诉你数据有多少行、多少列:
print(f'数据形状: {df.shape}')
print(f'行数: {df.shape[0]}')
print(f'列数: {df.shape[1]}')
输出:
数据形状: (1000000, 5)
行数: 1000000
列数: 5
100万行,5列。这个数据量对于Tick数据来说算小的。我处理过最大的一个文件,单日全市场Tick数据有8000万行,光读进来就花了10分钟。
为什么要检查形状?说白了,就是确认数据有没有被正确加载。如果预期有100万行,结果只读了80万行,那可能是文件被截断了,或者读取时跳过了某些行。
1.5 本章知识体系
下面这张图总结了数据读取与初步探索的核心流程:
这张图展示了从读取数据到发现问题的完整流程。你会发现,这其实是个循环——发现问题后,往往需要调整读取参数,重新加载数据。
1.6 实战小贴士
最后分享几个我在实战中总结的经验:
- 先看数据量:用shape确认行数是否合理。如果某天数据量突然少了一半,那可能是数据源出了问题。
- 检查时间范围:用df['timestamp'].min()和max()确认数据覆盖的时间段。我遇到过数据只覆盖了上午的情况——那天的下午行情全丢了。
- 关注内存占用:info()里会显示memory usage。如果内存占用太大,考虑用chunksize分块读取,或者只读需要的列。
- 保留原始数据:永远不要直接修改原始文件。我习惯把原始数据放在一个只读目录,所有操作都在副本上进行。
- 用pd.read_csv()读取Tick数据,记得优化dtype和parse_dates
- head()和tail()快速预览,注意检查文件末尾的脏数据
- info()查看列类型和非空数量,describe()查看统计分布
- shape确认数据形状,判断数据是否完整加载
好了,数据已经读进来了,也初步看过了。接下来就该进入真正的清洗环节了——处理缺失值、异常值、重复数据。不过那是下一章的事,咱们先把今天的内容消化好。
公众号:蓝海数据掘金营,微信deep3321