4. 探索性数据分析(EDA):数据分布分析、相关性分析、可视化洞察
做量化交易,尤其是订单流预测,最怕什么?
怕数据骗你。
模型跑得再漂亮,如果底层数据本身有问题,那结果就是垃圾。所以,在动手建模之前,我习惯先花大量时间做探索性数据分析(EDA)。说白了,就是跟数据「聊聊天」,看看它长什么样,脾气如何,有没有藏着什么猫腻。
4.1 为什么要做 EDA?
很多人一上来就撸模型,觉得EDA浪费时间。我以前也这么干过,结果模型死活不收敛,调参调到怀疑人生。后来发现,是数据里有个字段全是空值,另一个字段的分布严重偏态。
嗯,从那以后,我再也不敢跳过EDA了。
EDA的核心目的有三个:
- 发现数据质量问题:缺失值、异常值、重复数据,这些是家常便饭。
- 理解数据分布:价格、成交量、订单流不平衡值,它们服从什么分布?正态?偏态?
- 挖掘变量关系:哪些特征对预测订单流方向有帮助?哪些是噪音?
我的经验:EDA 不是一次性工作。每次新增特征或更换数据源,我都会重新跑一遍。你想想看,数据源换了,分布可能就变了,之前的假设可能全废。
4.2 数据分布分析
先看单变量的分布。我一般从「描述性统计」入手。
4.2.1 描述性统计
用 df.describe() 快速扫一眼,重点关注:
- 均值 vs 中位数:如果两者差距大,说明数据有偏态。
- 标准差:波动性大不大?订单流数据通常方差很大。
- 四分位数:有没有极端值?比如 75% 分位和最大值差了好几个数量级,那大概率有异常。
import pandas as pd
import numpy as np
# 假设 df 是订单流数据
print(df[['price', 'volume', 'order_flow_imbalance']].describe())
4.2.2 直方图与核密度估计
光看数字不够,得画图。我习惯用直方图 + 核密度曲线叠加,一眼就能看出分布形态。
import matplotlib.pyplot as plt
import seaborn as sns
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
sns.histplot(df['price'], kde=True, ax=axes[0])
axes[0].set_title('价格分布')
sns.histplot(df['volume'], kde=True, ax=axes[1])
axes[1].set_title('成交量分布')
sns.histplot(df['order_flow_imbalance'], kde=True, ax=axes[2])
axes[2].set_title('订单流不平衡值分布')
plt.tight_layout()
plt.show()
一个小技巧:如果分布严重偏态,可以考虑做对数变换或 Box-Cox 变换。我在处理成交量数据时经常用 log(volume+1),效果不错。
4.3 相关性分析
单变量看完了,接下来看变量之间的关系。相关性分析能帮你快速筛选出有用的特征。
4.3.1 皮尔逊相关系数
最常用的方法。但要注意,它只捕捉线性关系。如果数据是非线性的,皮尔逊系数可能很低,但实际有关系。
corr_matrix = df.corr(method='pearson')
print(corr_matrix['order_flow_imbalance'].sort_values(ascending=False))
4.3.2 热力图可视化
数字看多了眼花,热力图更直观。我一般把相关性矩阵画成热力图,颜色越深,相关性越强。
plt.figure(figsize=(10, 8))
sns.heatmap(corr_matrix, annot=True, cmap='coolwarm', fmt='.2f', linewidths=0.5)
plt.title('特征相关性热力图')
plt.show()
注意:相关性不等于因果。我曾经发现「订单流不平衡值」和「下一时刻价格变化」相关性高达 0.6,但实际回测时效果并不好。为什么?因为相关性中混杂了市场微观结构的噪音。所以,相关性分析只是线索,不是结论。
4.4 可视化洞察
EDA 的精髓在于「看」。有些模式,数字永远发现不了,但图上一眼就能看出来。
4.4.1 时间序列图
订单流数据是时序数据,所以第一件事就是画时间序列图。看看有没有趋势、季节性、或者突然的跳变。
plt.figure(figsize=(12, 4))
plt.plot(df['timestamp'], df['order_flow_imbalance'], alpha=0.7)
plt.title('订单流不平衡值随时间变化')
plt.xlabel('时间')
plt.ylabel('不平衡值')
plt.show()
4.4.2 箱线图与异常值检测
箱线图能快速识别异常值。我习惯把每个特征的箱线图并排画出来,看看哪些特征「尾巴」特别长。
plt.figure(figsize=(12, 6))
sns.boxplot(data=df[['price', 'volume', 'order_flow_imbalance', 'spread']])
plt.title('各特征箱线图')
plt.show()
4.4.3 散点图矩阵
如果你想看多个变量两两之间的关系,散点图矩阵是最佳选择。不过数据量大的时候会卡,建议先采样。
sample_df = df.sample(1000) # 采样1000条
sns.pairplot(sample_df[['price', 'volume', 'order_flow_imbalance', 'next_price_change']])
plt.show()
4.5 核心逻辑流程图
下面这张图,是我做 EDA 时脑子里始终绷着的一根弦。它帮我把整个流程串起来,不会漏掉关键步骤。
4.6 实战中的避坑指南
做 EDA 这么多年,踩过的坑不少。分享几个典型的:
- 不要只看 p-value:数据量大的时候,p-value 很容易显著,但实际效果可能微乎其微。我习惯同时看效应量(effect size)。
- 小心幸存者偏差:如果你只分析「成功交易」的数据,模型会学偏。记得把「失败交易」也加进来。
- 可视化要适度:图太多反而眼花。我一般控制在 5-8 张关键图,每张图讲清楚一个故事。
我的习惯:每次 EDA 结束后,我会写一份简短的报告,记录关键发现和决策。比如「成交量分布偏态严重,决定做 log 变换」、「订单流不平衡值与价格变化相关性 0.45,保留该特征」。这样下次回来还能记得当时为什么这么做。
好了,EDA 这部分就聊到这儿。数据看明白了,下一步建模心里才有底。记住,EDA 不是走过场,它是你和数据之间的第一次深度对话。聊好了,后面事半功倍。