4. 缺失值处理(上):识别缺失值、可视化与删除
各位同学,今天咱们来聊聊数据清洗里最让人头疼,但也最基础的问题——缺失值。说实话,我做了这么多年量化投资,见过太多因为缺失值没处理好,导致整个模型崩掉的案例。你想想看,数据里缺了一块,就像盖房子少了几块砖,后面再怎么折腾也白搭。
这一节,咱们先搞定缺失值的识别、可视化,以及最粗暴但有时候也最有效的处理方式——直接删除。嗯,别急着跳过,这里面的门道可不少。
4.1 识别缺失值:isnull() 与 isnull().sum()
拿到一份数据,第一件事是什么?我个人习惯,先看看数据长什么样,尤其是哪些地方是空的。Pandas 里最常用的就是 isnull() 这个方法。
isnull() 会返回一个和原 DataFrame 形状一样的布尔型 DataFrame。每个单元格如果是缺失值,就标为 True,否则是 False。说白了,就是给你画一张「缺失地图」。
import pandas as pd
import numpy as np
# 模拟一份景气度数据
df = pd.DataFrame({
'日期': ['2024-01', '2024-02', '2024-03', '2024-04'],
'PMI': [50.2, np.nan, 51.0, np.nan],
'工业增加值': [6.1, 5.8, np.nan, 6.3],
'社融规模': [4.5, np.nan, np.nan, 5.1]
})
# 识别缺失值
print(df.isnull())
输出结果会告诉你,哪些位置是空的。但光看这个还不够直观,我们更想知道每列有多少缺失值。这时候 isnull().sum() 就派上用场了。
# 统计每列缺失值数量
print(df.isnull().sum())
输出类似这样:
日期 0
PMI 2
工业增加值 1
社融规模 2
dtype: int64
你看,一目了然。PMI 和社融规模各缺了 2 个,工业增加值缺了 1 个。日期列是完整的。我在项目中遇到过,有些新手一上来就 dropna() 全删了,结果发现删掉了 80% 的数据,那还分析个啥?所以,先摸清缺失值的家底,是第一步。
df.isnull().sum() / len(df) * 100。这样能更直观地判断缺失是否严重。
4.2 缺失值可视化:missingno 库
光看数字有时候不够直观。你想想看,如果数据有几百列,你一个个看 sum() 的结果,眼睛不花才怪。这时候,可视化就派上用场了。我强烈推荐 missingno 这个库,专门用来画缺失值。
安装很简单:
pip install missingno
然后咱们来看看怎么用:
import missingno as msno
import matplotlib.pyplot as plt
# 用矩阵图展示缺失值
msno.matrix(df)
plt.show()
这个矩阵图里,白色的地方就是缺失值,黑色的地方是正常值。一眼就能看出哪些列缺失严重,以及缺失值之间有没有关联。比如,如果 PMI 和社融规模同时缺失,那可能这两个数据来源是同一个渠道出了问题。
还有一个更实用的图叫 条形图:
msno.bar(df)
plt.show()
这个图直接显示每列的非缺失值数量,相当于把 isnull().sum() 画成了柱状图。我个人习惯,拿到数据先跑一个 msno.matrix(),再跑一个 msno.bar(),基本就能对数据质量有个全局把握了。
heatmap 功能一看,发现这两个变量的缺失模式高度相关。原来是因为那个月统计局调整了统计口径,导致部分数据延迟发布。如果不做可视化,光看数字根本发现不了这个规律。
4.3 删除缺失值:dropna()
好了,识别完了,也看清楚了。接下来怎么办?最直接的方法就是——删掉。Pandas 提供了 dropna() 方法,可以删除包含缺失值的行或列。
基本用法:
# 删除所有包含缺失值的行
df_clean = df.dropna()
print(df_clean)
输出结果:
日期 PMI 工业增加值 社融规模
0 2024-01 50.2 6.1 4.5
你看,只有第一行是完整的,其他行都被删掉了。这在实际项目中往往太激进。你想想看,如果数据本来就少,这么一删可能就剩个零头了。
所以,dropna() 有几个参数可以精细控制:
axis=0:默认按行删除(axis=0),也可以按列删除(axis=1)。how='any':默认只要有一个缺失值就删除。可以改成how='all',只有整行全为空时才删除。thresh:指定非缺失值的最小数量。比如thresh=3表示该行至少有 3 个非缺失值才保留。subset:只检查某些列是否有缺失值。
举个例子:
# 只删除 PMI 和 社融规模 这两列都缺失的行
df_clean2 = df.dropna(subset=['PMI', '社融规模'], how='all')
print(df_clean2)
输出:
日期 PMI 工业增加值 社融规模
0 2024-01 50.2 6.1 4.5
1 2024-02 NaN 5.8 NaN
3 2024-04 NaN 6.3 5.1
你看,第 2 行(索引 2)因为 PMI 和社融规模都缺失,被删掉了。但第 1 行和第 3 行虽然也有缺失,但至少有一列是完整的,所以保留了下来。这在处理多变量数据时非常实用。
df.dropna(inplace=True),结果把整个数据集删得面目全非。建议先备份原始数据,或者用 df.copy() 创建一个副本再操作。记住,数据删了就回不来了。
4.4 知识体系总览
为了让你更直观地理解这一节的内容,我画了一张流程图,把缺失值处理的逻辑串起来:
这张图把咱们这一节的内容串起来了。先识别,再判断缺失严重程度,然后决定是可视化深入分析,还是直接统计。最后,如果决定删除,就用 dropna() 精细操作。如果不删除,那就留到下一节讲填充方法。
4.5 实战小贴士
最后,分享几个我在实际项目中总结的经验:
- 先备份,再操作:任何时候,对原始数据做修改前,先
df_backup = df.copy()。这是血的教训。 - 不要盲目删除:如果缺失值占比超过 30%,删除可能不是好主意。这时候要考虑填充或者用模型预测。
- 注意缺失模式:用 missingno 的
heatmap看看缺失值之间有没有相关性。如果有,说明缺失不是随机的,背后可能有系统性原因。 - 记录你的操作:我习惯在代码里加注释,记录每一步删除了多少行、为什么删。这样复盘时能快速定位问题。
好了,这一节的内容就到这里。缺失值识别和删除是数据清洗的基本功,但也是最容易出错的地方。你想想看,如果连数据里缺了什么都不知道,后面再高级的分析也是空中楼阁。下一节咱们会讲缺失值的填充方法,那才是真正考验功力的地方。
公众号:蓝海资料掘金营,微信deep3321