3、缺失值处理(上):缺失值产生的原因、缺失值可视化(missingno库)、删除缺失值(dropna)

各位同学,欢迎来到量化因子计算的第三节课。

上一章我们聊了数据清洗的准备工作,包括格式统一和异常值识别。今天咱们进入一个更核心的话题——缺失值处理

说实话,做量化最怕什么?不是策略亏钱,而是数据里藏着坑。缺失值就是最常见的坑之一。我见过不少新手,因子算出来回测曲线漂亮得不行,结果一上实盘就崩——后来发现是数据缺失导致的计算偏差。

所以,缺失值处理不是可选项,是必选项。今天我们先讲上半部分:缺失值怎么来的、怎么可视化、以及最粗暴的处理方式——删除。

3.1 缺失值产生的原因

先聊聊缺失值是怎么来的。你想想看,股票数据按理说应该是连续的,为什么会有空值?

我个人总结,主要有这么几类原因:

  • 停牌或退市:股票停牌期间,交易数据自然就断了。这是A股最常见的缺失原因。
  • 数据源问题:有些数据供应商采集不全,或者接口偶尔抽风。我在项目中遇到过,某家数据商在2015年股灾期间,有将近20%的股票日频数据是缺失的。
  • 计算过程中的NaN:比如计算收益率时,分母为0;或者计算滚动指标时,窗口期不够长。这些都会产生缺失值。
  • 因子本身特性:有些因子只适用于特定股票,比如次新股的一些指标,老股票就没有。

核心观点:缺失值不是随机出现的。它背后往往有某种逻辑。理解缺失的原因,比单纯处理缺失更重要。

举个例子,如果某只股票连续缺失3天数据,大概率是停牌了。这时候你直接删除,可能会丢掉重要的重组信息。嗯,这里要注意——不要无脑删除

3.2 缺失值可视化:missingno库

做数据清洗,我习惯先看一眼缺失值的分布。用眼睛看,比用代码算更直观。

Python里有个专门干这事的库——missingno。它能把缺失值画成热力图、条形图、矩阵图,一目了然。

安装很简单:

pip install missingno

然后我们来看一个实际案例。假设你有一个DataFrame,包含多只股票的日频数据:

import pandas as pd
import missingno as msno
import matplotlib.pyplot as plt

# 模拟数据
df = pd.read_csv('stock_data.csv')

# 缺失值矩阵图
msno.matrix(df)
plt.show()

运行之后,你会看到一张黑白相间的图。白色代表有数据,黑色代表缺失。一眼就能看出哪些列缺失严重,哪些行是空白的。

我个人最喜欢的是热力图

msno.heatmap(df)
plt.show()

这张图能告诉你:哪些列的缺失是相关的。比如,如果volumeturnover同时缺失,说明它们可能来自同一个数据源。这在排查数据问题时特别有用。

小技巧:如果数据量太大,可以抽样后再画图。不然图会糊成一团,啥也看不清。

还有一个条形图,展示每列的完整度百分比:

msno.bar(df)
plt.show()

这个图适合快速判断:哪些列可以留,哪些列必须扔。

3.3 删除缺失值:dropna

好了,看完缺失值的分布,接下来就是处理了。最直接的方法——删除

Pandas里用dropna()方法。用法很简单,但参数有讲究。

3.3.1 基本用法

# 删除任何包含缺失值的行
df_clean = df.dropna()

# 删除任何包含缺失值的列
df_clean = df.dropna(axis=1)

注意,dropna()默认是删除行。而且只要某一行有一个NaN,整行都会被删掉。这很暴力。

3.3.2 按阈值删除

有时候我们不想那么绝情。比如,某行只缺失了一个值,删掉太可惜。这时候可以用thresh参数:

# 保留至少有10个非空值的行
df_clean = df.dropna(thresh=10)

# 保留至少有80%非空值的行
df_clean = df.dropna(thresh=int(len(df.columns) * 0.8))

这个参数很实用。我在做多因子模型时,经常设置thresh=0.7 * 总列数,意思是:只要某行有超过30%的数据是缺失的,就删掉。低于30%的,后面再用插值补。

3.3.3 按列删除

有时候是某列缺失太严重,比如缺失率超过50%,那这列留着也没意义了。

# 删除缺失率超过50%的列
df_clean = df.dropna(thresh=len(df) * 0.5, axis=1)

避坑指南:我曾经在回测时,因为删除了某只停牌股票的整行数据,导致回测时间序列不连续,策略信号出现了跳变。后来我改用thresh参数,只删除缺失严重的行,才解决了这个问题。

3.3.4 按子集删除

有时候我们只关心某些关键列。比如,计算因子时,closevolume是必须的,其他列可以容忍缺失。

# 只在close或volume缺失时才删除该行
df_clean = df.dropna(subset=['close', 'volume'])

这个用法在因子计算中非常常见。你想想看,如果close缺失了,那收益率、波动率这些都没法算,留着也没用。

3.4 什么时候该删,什么时候不该删?

删除缺失值虽然简单,但不是万能的。我总结了几条经验:

情况 建议 原因
缺失率 < 5% 可以删除 影响很小,删除最省事
缺失率 5%~30% 视情况而定 如果数据量够大,可以删;否则建议插值
缺失率 > 30% 不建议删除 删除太多会破坏数据分布,建议先分析原因
关键字段缺失 必须处理 比如close、volume,缺失了就没法算因子
非关键字段缺失 可以保留 后续用插值或填充处理

说白了,删除缺失值就像做手术——小病可以切,大病切了反而更糟。你得先诊断清楚。

3.5 本章小结

今天我们聊了缺失值处理的三个核心点:

  • 缺失值产生的原因:停牌、数据源问题、计算过程、因子特性。理解原因才能对症下药。
  • 缺失值可视化:用missingno库画矩阵图、热力图、条形图。先看再处理,这是好习惯。
  • 删除缺失值:dropna的四种用法——全删、按阈值删、按列删、按子集删。记住,删除不是万能的。

下一章我们会讲缺失值处理的另一种思路——填充和插值。到时候你会看到,有些缺失值其实是可以「救」回来的。

好了,今天的课就到这里。代码不多,但思路很重要。建议你打开自己的数据,用missingno画几张图看看,说不定能发现一些有趣的东西。

一句话总结:缺失值处理,先看原因,再可视化,最后决定删不删。别一上来就dropna(),那是新手干的事。

缺失值处理(上)知识体系 缺失值处理 缺失值产生原因 停牌/退市 数据源问题 计算过程NaN 因子特性限制 缺失值可视化 矩阵图 热力图 条形图 missingno库 删除缺失值 全删(默认) 按阈值删 按列删 按子集删 核心原则:先分析原因 → 再可视化 → 最后决定是否删除

蓝海数据掘金营,专注资料整理