第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 本章知识体系

下面这张图总结了数据读取与初步探索的核心流程:

Tick数据读取与初步探索流程 1. 读取数据 pd.read_csv() 2. 快速预览 head() / tail() 3. 数据概览 info() / describe() 4. 检查形状 shape属性 5. 发现问题 缺失值 / 异常值 6. 决定下一步 清洗 / 分析 / 特征工程 发现问题后返回步骤1,调整读取参数

这张图展示了从读取数据到发现问题的完整流程。你会发现,这其实是个循环——发现问题后,往往需要调整读取参数,重新加载数据。

1.6 实战小贴士

最后分享几个我在实战中总结的经验:

  • 先看数据量:用shape确认行数是否合理。如果某天数据量突然少了一半,那可能是数据源出了问题。
  • 检查时间范围:用df['timestamp'].min()和max()确认数据覆盖的时间段。我遇到过数据只覆盖了上午的情况——那天的下午行情全丢了。
  • 关注内存占用:info()里会显示memory usage。如果内存占用太大,考虑用chunksize分块读取,或者只读需要的列。
  • 保留原始数据:永远不要直接修改原始文件。我习惯把原始数据放在一个只读目录,所有操作都在副本上进行。
🔑 核心要点
  1. 用pd.read_csv()读取Tick数据,记得优化dtype和parse_dates
  2. head()和tail()快速预览,注意检查文件末尾的脏数据
  3. info()查看列类型和非空数量,describe()查看统计分布
  4. shape确认数据形状,判断数据是否完整加载

好了,数据已经读进来了,也初步看过了。接下来就该进入真正的清洗环节了——处理缺失值、异常值、重复数据。不过那是下一章的事,咱们先把今天的内容消化好。


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