1. 缺失值可视化:为什么你需要missingno?

做数据分析的朋友们,肯定都遇到过数据缺失这个“老大难”问题。你辛辛苦苦从数据库里导出数据,或者从网上爬下来一堆信息,打开一看,好家伙,到处都是空值(NaN)。这时候,你该怎么办?直接删掉?用平均值填充?还是用更复杂的算法来预测?

别急,在做任何处理之前,有一件更重要、更基础的事情:你得先看清楚你的数据到底“缺”成了什么样子。缺失值是随机分布的,还是集中在某几个特征上?不同特征的缺失有没有关联?这些缺失会不会影响你后续的分析模型?这些问题,光靠 df.isnull().sum() 打印出一堆数字,是很难有直观感受的。数字是冰冷的,而图表是鲜活的。

这就是 missingno 这个Python库大显身手的地方。它不帮你填充数据,也不做复杂的统计推断,它的任务只有一个:用最直观的图形,把你数据缺失的“惨状”一目了然地画出来。我用了这么多年,感觉它就像数据分析前的“体检报告”,能让你快速诊断数据的健康状况,避免在脏数据上白费功夫。

今天,我就以经典的波士顿房价数据集为例,带你深度体验 missingno 的四种核心可视化“武器”:矩阵图、条形图、热力图和树状图。我会详细对比它们各自擅长什么场景,怎么解读,以及在实际项目中我踩过哪些坑。保证你看完就能上手,下次再遇到缺失值,心里再也不慌。

2. 环境准备与数据“造缺”

工欲善其事,必先利其器。咱们先从最基础的安装和准备数据开始。这部分虽然简单,但有几个小细节不注意,后面可能就会出问题。

2.1 安装与导入

安装 missingno 非常简单,一行命令搞定。我习惯在虚拟环境里操作,这样项目之间的依赖不会打架。

pip install missingno

安装好后,在Python脚本或Jupyter Notebook里导入它。通常我们会一起导入数据分析的“三剑客”:pandas, numpy,以及 missingno 本身。

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

# 为了让图表在Notebook里直接显示
%matplotlib inline

这里有个小提示:missingno 是基于 matplotlib 来画图的,所以确保你的环境里 matplotlib 也已经安装好了。如果图表显示中文有问题,可能还需要额外设置一下字体。

2.2 数据加载与人工制造缺失值

我们选用 sklearn 里的波士顿房价数据集。这个数据集很干净,本身没有缺失值,但这恰恰给了我们“做实验”的机会。在真实项目中,你拿到的是已经有缺失的数据,而在这里,我们需要手动“挖掉”一些值,来模拟真实情况。

from sklearn.datasets import load_boston

# 加载数据
boston = load_boston()
data = boston['data']
target = boston['target']

# 将特征数据和目标变量合并成一个DataFrame
# 给特征列起个名,方便识别
feature_names = [f'feature_{i}' for i in range(data.shape[1])]
df = pd.DataFrame(data, columns=feature_names)
df['house_price'] = target  # 目标变量:房价中位数

print(f"数据集形状: {df.shape}")
print(df.head())

现在 df 是一个完整的、没有缺失的数据框。为了演示,我们得故意弄点“残缺美”。一个常见的做法是,随机将某些值替换为 np.nan。但为了更贴近某些真实场景(比如传感器在特定条件下不工作),我更喜欢用条件替换。比如,我们假设 feature_1(可能是人均犯罪率)和 feature_3(可能是非零售商业用地比例)这两个特征,当它们的原始值为0时,数据采集可能失败了,因此我们将其视为缺失。

# 复制一份数据,避免污染原数据
df_with_missing = df.copy()

# 将feature_1和feature_3中等于0的值设置为NaN
df_with_missing['feature_1'] = df_with_missing['feature_1'].replace(0, np.nan)
df_with_missing['feature_3'] = df_with_missing['feature_3'].replace(0, np.nan)

# 检查缺失情况
missing_summary = df_with_missing.isnull().sum()
print("各特征缺失值数量:")
print(missing_summary[missing_summary > 0])

好了,现在我们手头就有了一份带“伤”的数据。接下来,就请出我们今天的主角,用四种不同的视角来审视这些缺失。

3. 全局概览:矩阵图 (msno.matrix)

当你第一次拿到一个有缺失值的数据集时,我强烈建议你第一个使用的就是 msno.matrix。它能给你一个最宏观、最直观的全局印象。

3.1 基本用法与解读

使用起来非常简单,直接把你的 DataFrame 丢进去就行。

msno.matrix(df_with_missing, figsize=(10, 6))
plt.show()

这张图看起来有点像音乐软件的频谱图。我来带你解读一下:

  • 黑白条纹:每一行代表一条数据样本(在这里是一条街区的信息),每一列代表一个特征。黑色线条表示该位置有数据,白色线条(或间隙)表示该位置是缺失值。所以,一眼扫过去,哪一列的白线多,就说明哪个特征缺得厉害。
  • 最右侧的“火花线”:这是矩阵图的精髓!它把每一行的缺失情况压缩成一条线。如果某一行数据完整,没有缺失,那么这条线上对应位置就是全黑。如果某一行有缺失,就会在缺失特征对应的位置出现白点。通过这条“火花线”,你可以快速看出缺失值是随机分散在各行,还是集中在某几行。如果白点集中在火花线某一段,说明缺失有“扎堆”现象。
  • 坐标轴信息:左边纵轴是样本索引,告诉你看了多少条数据。图右下角通常会有两个数字,比如“12 of 14 columns”,这表示在全部14个特征列中,有12列是完全没有缺失值的。这是一个快速的完整性统计。

3.2 实战技巧与踩坑点

在实际项目中,数据量可能很大,直接画矩阵图会黑压压一片。missingno 提供了 sample 参数,可以随机抽样显示。

# 只随机显示100个样本,让图更清晰
msno.matrix(df_with_missing.sample(100), figsize=(10, 6))
plt.show()

另一个我常用的参数是 labels=True,它会把所有特征名显示在横轴上,避免和旁边的颜色条混淆。

msno.matrix(df_with_missing, labels=True, figsize=(12, 6))
plt.show()

我踩过的一个坑:有时候数据集的索引不是简单的0-N,而是时间戳或者其他复杂索引。直接画图可能导致横轴标签重叠,一塌糊涂。这时候,可以先 reset_index() 或者只选择部分特征列来画图。矩阵图最适合在你数据分析的第一步使用,它能让你在几秒钟内对数据缺失的规模和模式有一个定性认识,决定下一步是清洗、填充还是直接删除某些行或列。

4. 定量分析:条形图 (msno.bar)

如果说矩阵图给了你一个定性、直观的感受,那么 msno.bar 就提供了精确的定量描述。它是我在写数据报告时最常用到的图表,因为数字最能说服人。

4.1 生成与理解条形图

调用同样简单:

msno.bar(df_with_missing, figsize=(10, 6), color='steelblue')
plt.show()

这张条形图非常容易理解:

  • 每个柱子:代表一个特征列。
  • 柱子的高度:表示该列非缺失值的数量。所以,柱子越高,说明这个特征的数据越完整。
  • 左侧纵坐标:显示的是非缺失值所占的百分比。从0%到100%,一目了然。比如,如果 feature_1 的柱子顶端对应着左侧的80%,就说明这个特征有80%的数据是存在的,缺失了20%。
  • 右侧纵坐标:对应的是非缺失值的实际数量。这个和左侧百分比是联动的。
  • 图上方的数字:每个柱子顶端会标出具体的非缺失值数量,非常贴心。

4.2 在数据清洗决策中的应用

条形图的强大之处在于它能直接指导你的数据清洗策略。我通常会根据它来做一些快速决策:

  1. 决定删除阈值:如果某个特征的缺失率超过了一个阈值(比如50%或80%),我会慎重考虑是否直接删除这个特征。因为缺失太多,即使填充,引入的噪声也可能远大于信息量。条形图让我一眼就能找到这些“重灾区”。
  2. 评估样本完整性:通过观察每个特征的完整性,我可以反向推断哪些样本行是“优质”的。比如,如果大部分特征都完整,只有一两个特征缺失率很高,我可能会选择删除这些特征,而不是删除大量样本。
  3. 汇报与沟通:在团队会议或向非技术背景的同事解释数据问题时,一张清晰的条形图比十行代码输出都有用。你可以指着图说:“看,我们有三个特征缺失比较严重,尤其是这个,缺了30%,我们需要讨论一下是找源头补数据,还是用算法填充。”

你可以通过 sort 参数让柱子按完整性排序,这样最需要关注的特征就排在了前面。

msno.bar(df_with_missing, sort='ascending', figsize=(10, 6)) # 按缺失率升序排列,缺失最多的在左边
plt.show()

5. 探索关联:热力图 (msno.heatmap)

前面的方法告诉我们“缺了多少”,而 msno.heatmap 则试图回答一个更深入的问题:这些缺失,有关联吗? 也就是说,一个特征缺失了,会不会导致另一个特征也更容易缺失?发现这种关联性对于理解数据缺失的机制至关重要。

5.1 热力图原理揭秘

热力图展示的是特征之间缺失值的相关性,更准确地说,是“无效相关性”。它的计算方式是:对于任意两个特征A和B,计算在所有A缺失的数据行中,B也缺失的比例。这个值在-1到1之间,但在这里,我们主要关注0到1的正相关。

  • 值 = 1:完全正相关。意味着只要A缺失,B就一定缺失;只要B缺失,A也一定缺失。这强烈暗示两个特征的缺失是由同一个原因导致的(比如,来自同一个有故障的传感器,或者同一次问卷中未被回答的板块)。
  • 值 = 0:无相关性。特征A的缺失与特征B的缺失完全独立,是随机发生的。
  • 值 = -1:完全负相关。这意味着A缺失时,B总是存在的,反之亦然。这种情况在实际中比较少见。

5.2 结果分析与场景判断

让我们对处理过的波士顿数据画热力图:

msno.heatmap(df_with_missing, figsize=(8, 6), cmap='RdBu')
plt.show()

由于我们只对 feature_1feature_3 制造了缺失,而且制造逻辑是独立的(各自把0换成NaN),所以这张热力图里大概率看不到明显的颜色块,所有格子都是接近0的浅色。这说明缺失是随机、独立的。

让我分享一个真实案例:有一次分析用户问卷数据,发现“收入”和“职业”两个字段缺失率都很高。用热力图一看,它们之间的相关性高达0.9!这就不太可能是随机漏填了。进一步调查发现,问卷中这两个问题是放在同一页的,很多用户觉得涉及隐私,直接跳过了整页。这个发现让我们重新设计了问卷流程,把敏感问题拆开并放在了不同位置。所以,热力图帮你找到的不仅是数据问题,有时还能揭示产品设计或数据收集流程上的缺陷。

需要注意:热力图计算的是缺失的共现关系,而不是特征值本身的相关性。不要把它和 df.corr() 得到的皮尔逊相关系数矩阵搞混了。后者关心的是“有值”的部分之间的关系,而热力图关心的是“没值”的部分之间的关系。

6. 识别模式:树状图 (msno.dendrogram)

最后这个工具 msno.dendrogram,可以说是 missingno 里的“高级玩家”。它用一种更智能的方式——层次聚类,来帮你对特征进行分组,揭示更深层次的缺失模式。

6.1 树状图如何工作

树状图基于特征之间缺失值的相似性进行聚类。简单来说,它计算每两个特征之间缺失模式的“距离”。如果两个特征的缺失总是发生在同一些样本行上,它们的距离就很近,会被归到同一个分支下。

msno.dendrogram(df_with_missing, figsize=(10, 6))
plt.show()

看图的方法是从右往左:

  • 最右侧的叶子节点:每个叶子代表一个特征。
  • 枝干的连接点(节点):高度代表了两个特征(或两个特征组)之间的“距离”。枝干越短,说明它们之间的缺失模式越相似
  • 颜色条:图右侧的颜色条提供了一个参考尺度,将距离映射到颜色上。距离为0(完全相似)通常用一种颜色表示,距离越大,颜色逐渐变化。

6.2 在复杂数据模式下的应用

在我们这个简单的例子里,feature_1feature_3 会被聚到一类(因为它们都有缺失),而其他完整的特征会被聚到另一类(距离为0)。

但在真实的多维数据中,树状图能发挥更大威力。比如,你有一个包含几十个特征的用户行为数据集,缺失情况乱七八糟。通过树状图,你可能会发现:

  • 特征A、B、C总是抱团出现缺失。
  • 特征D、E独立于其他特征缺失。
  • 剩下的特征F到Z几乎完全完整,自成一体。

这个聚类结果能给你带来非常宝贵的洞见。抱团缺失的特征组,可能对应着同一个数据源、同一个处理步骤,或者用户行为的同一个环节。你可以针对这一个“组”制定统一的数据清洗或填充策略,而不是对每个特征单独处理,效率大大提高。

一个实用建议:树状图在特征数量较多(比如超过15个)时效果更明显。如果特征很少,聚类结果可能看起来比较 trivial。它可以作为你进行特征工程或设计数据插补策略的前置探索步骤。

7. 方法对比与实战选型指南

一口气介绍了四种方法,你可能有点晕。到底该用哪个?别急,我帮你总结了一张对比表,并给出我的实战选型心得。

可视化方法 核心功能 最佳使用场景 优点 缺点
矩阵图 (matrix) 全局直观展示缺失值的分布和样本完整性。 数据探索的第一步,快速了解缺失规模与模式。 最直观,能同时看到样本和特征维度的缺失。 数据量极大时图会显得拥挤,不适合精确量化。
条形图 (bar) 精确量化每个特征的缺失率/完整率。 数据质量报告、决定特征删除/保留的阈值判断。 提供精确数字,支持排序,沟通效率高。 无法展示缺失值之间的关联性或样本层面的模式。
热力图 (heatmap) 揭示特征之间缺失值的相关性 探索缺失是否由共同原因导致,发现数据收集流程问题。 能发现隐藏的关联模式,指导根本原因分析。 在缺失完全随机或特征很多时,图可能信息量不大。
树状图 (dendrogram) 基于缺失模式对特征进行层次聚类 处理高维数据,对特征进行分组,制定分组清洗策略。 提供更结构化的洞察,有助于特征工程。 解读需要一定的聚类分析知识,在简单数据上不必要。

我的常规分析流程是这样的

  1. 第一步,用 msno.matrix。像看地图一样先扫一眼全局,心里有个底。如果数据行太多,就抽样看。
  2. 第二步,用 msno.bar。拿到确切的缺失百分比,用数据说话。我会把缺失率超过30%的特征标红,重点讨论。
  3. 如果缺失情况比较严重或复杂,进入第三步。我会先后使用 msno.heatmapmsno.dendrogram。先看热力图有没有强关联,再用树状图看能不能把特征分个组。这个过程能帮我形成清洗策略:是删除整列?还是用同一组的其他特征来预测填充?或者是删除缺失集中的那些行?

记住,没有一种方法是万能的。它们就像一套组合工具,帮你从不同角度把数据缺失的问题看得清清楚楚、明明白白。工具用熟了,你处理数据问题的信心和效率都会大大提升。下次遇到满是NaN的数据集,别再头疼了,打开Python,从 import missingno as msno 开始你的数据诊断之旅吧。

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐