☰
Pandas DataFrame缺失值处理全攻略:从检测到填充的实战指南
2026/10/3 2:47:45 网站建设 项目流程

1. 缺失值问题为什么值得单独写一篇:从一次真实的数据分析翻车说起

做Python数据分析的同行应该都有这种经历:拿到一份看起来挺规整的表格,跑完df.describe()一看,计数比预期少了一截,或者画完分布图发现某个特征莫名少了一块——这个时候基本可以断定,数据里有缺失值。我见过不少初学者在处理缺失值时,随手敲一行df.dropna()就把数据全删了,结果后续的聚合统计、机器学习建模全跑偏,还找不到问题出在哪。

这个系列更新到第047篇,我特意挑“DataFrame处理缺失值”单独开一篇,就是因为这个问题在真实项目里太常见了,而且处理方式的差异直接决定分析结论靠不靠谱。像我之前做过一个销售漏斗分析,订单表里的“成交金额”字段大约有6%的空值,如果直接删掉这些行,高客单价的样本占比会发生偏移;如果用全表平均值填充,又会把几个异常大额订单的影响放大到整列。最后我按业务维度拆开处理,效果才正常。

这篇博文的目的很明确:把Pandas中DataFrame处理缺失值的完整方法论讲透,包括缺失值是怎么产生的、如何快速摸清缺失家底、删除与填充各自的适用边界、插值法和前后向填充的场景选择,以及一套可以直接复用的实战流程。适合正在学Python数据分析、被缺失值问题困扰过的读者,也适合准备用Pandas做数据清洗的从业者参考。掌握之后,你再遇到“某列缺了20%数据怎么办”这种问题,就不会慌,也不会瞎填。

2. DataFrame里缺失值到底长什么样:np.nan、None和NaT的底层差异

2.1 三种缺失标记的“长相”与类型

刚接触Pandas的人最容易懵的一件事是:缺失值不只是一个“空”概念,它在DataFrame里其实有好几种表现形式,而且类型不同,处理方式也有细微差别。最常见的三种是numpy.nan、Python内置的None,以及时间类型里的NaT。

先看一个最直观的示例:

import numpy as np import pandas as pd df = pd.DataFrame({ "数值列": [1, 2, np.nan, 4], "混合列": [1, None, 3, 4], "文本列": ["a", "b", None, "d"] }) print(df.dtypes)

输出结果里,数值列是float64,文本列是object,混合列也是object。这里有一个关键细节:np.nan本质是一个浮点数,所以它会强制把整列提升为float64;而None是Python的NoneType对象,存进Pandas后如果该列还有其他Python对象(比如字符串),整列就变成object类型。

这个差异不是纸上谈兵。比如你有一列整数ID,读进来之后发现id列显示为float64,还带小数点的.0,十有八九就是这一列存在np.nan。我遇到过有人以为数据源出了问题,实际上就是缺失值把整数列“撑”成了浮点列。

2.2 为什么不能用 == 判断缺失

很多人第一反应是想用df[df["列"] == np.nan]去筛缺失值,结果返回空表,然后陷入困惑。原因是np.nan有一个反直觉的特性:它不等于任何值,包括它自己。

print(np.nan == np.nan) # False

这带来一个实际影响:明明数据里有空值,你用等值判断却什么都查不到。正确姿势是使用Pandas专门提供的检测函数isna()或isnull()。这两个名字不同,但底层是同一个东西——isnull是isna的别名,没有区别,notna()和notnull()同理,只是语义上一个强调“缺失”,一个强调“存在”。

再看一眼NaT。这是时间序列数据里的缺失标记,全称是“Not a Time”。当你有一列datetime64类型的数据存在缺失时,Pandas会用NaT表示。它同样不能用等值比较去筛选,也必须用isna()来识别。有一点值得注意:NaT可以与np.nan共存于同一列吗?不能。如果一列是datetime类型,缺失值会统一显示为NaT,不会出现nan。

2.3 不同类型缺失对后续计算的影响

np.nan在聚合计算中默认会被跳过,这个设计很贴心。比如df["数值列"].mean(),如果直接用Python内置的sum去算会得到nan,但Pandas的mean()方法会忽略缺失值。不过要注意,这个“忽略”只对部分方法有效,比如sum、mean、median都会跳过;但如果你想统计缺失值本身,就得用count(),它返回的是非缺失数量,两者语义别混。

None的情况稍微不同。如果一列是object类型,里面混着None和数字,你直接对这列做sum()会报错,因为Python没法把None和数字相加。这就是为什么数据清洗阶段,我们通常先把None替换成np.nan,统一缺失值的表示形式,再做后续处理。

实操中我的习惯是:拿到DataFrame先把所有None统一转成np.nan,用df.replace(None, np.nan)或者直接df = df.astype(object).where(pd.notna(df), np.nan)等方案处理。统一表示后,后续所有缺失值逻辑只跟np.nan打交道,心智负担小很多。

3. 动手前的家底盘点:用isna、info和缺失率矩阵量化缺失情况

3.1 五个快速盘点缺失值的核心方法

处理缺失值的第一步不是“修”,而是“查”。查清楚哪些列有缺失、缺多少、缺失率是多少,这一步决定了接下来该用删除还是填充策略。我把常用的盘点方法整理成一张表,方便对照记忆:

方法/属性作用典型用法
df.info()打印每列非空计数与数据类型,快速看全局df.info()
df.isna()返回布尔DataFrame,逐格标记缺失df.isna()
df.isna().sum()每列缺失总数df.isna().sum()
df.isna().mean()每列缺失率,结果更直观df.isna().mean()
df.isna().sum(axis=1)每行缺失总数,用于判断“坏行”df.isna().sum(axis=1)

df.info()是最快的起点。运行之后,Pandas会显示每一列的非空计数(Non-Null Count)和数据类型。只要看到非空计数小于总行数,这一列就有缺失。这个方法在数据量大的时候会比isna().sum()更快,因为它用的是底层元数据,不用逐格扫描。不过它只告诉你“每列缺多少”,不会告诉你是哪几行缺。

isna().sum()和isna().mean()是前面方法的细化版。我通常直接看均值,因为缺失率比绝对数更能辅助决策:如果一列缺失率只有0.5%,删除这几行通常没影响;如果缺失率高达40%,那这列本身就要考虑是否直接放弃。

3.2 缺失率的“行视角”与“列透视”

不要只看列,行视角同样重要。.isna().sum(axis=1)返回每一行的缺失个数,配合排序就能快速锁定“缺得特别多”的坏行。

# 计算每行缺失数,并取缺失最严重的5行 missing_per_row = df.isna().sum(axis=1) print(missing_per_row.sort_values(ascending=False).head(5))

如果某一行缺失了80%以上的字段,它在大多数分析场景下都是噪声,删掉它比填充更划算。另一个常用操作是查看“哪些列同时存在缺失”——这能帮你判断缺失是否成群出现。比如用户维度的“收入”和“职业”常常同时缺失,说明这两列缺失可能同源,处理时可以一并考虑。

3.3 用可视化快速理解缺失结构

当数据列很多时,纯数字会看得头晕,我用过一个非常轻量的方案:把df.isna()的结果做个热图。虽然这篇标题不涉及复杂可视化,但一行代码就能大幅提升判断效率:

import matplotlib.pyplot as plt import seaborn as sns plt.figure(figsize=(12, 6)) sns.heatmap(df.isna(), cbar=False, yticklabels=False) plt.show()

热图上的亮色条带就是缺失位置。如果缺失呈现“整段连续”形态,多半来自采集断档;如果散点分布,则更像随机缺失。这种直观感受会直接影响你后续选ffill还是dropna。当然,这只是辅助手段,最终决策还得结合业务判断。

我先说明:这一阶段的核心产出是一份“缺失清单”——每一列名、缺失数、缺失率、疑似缺失原因。有了这份清单,后面每一步处理都有据可依,而不是拿到数据就开始瞎填。

4. dropna()删除策略的边界:什么时候该删、删多狠、thresh参数怎么用

4.1 删除的时机判断

dropna()是最直接的缺失值处理方式:把有缺失的行或列直接丢弃。但很多人误以为这是万能药,实际上它的适用场景非常有限。我的经验是,只有以下三种情况才优先考虑删除:

  • 缺失率极低(一般低于5%)且整体样本量较大,删除后不影响数据分布。
  • 某一行缺失字段过多,几乎没有分析价值,比如一行40个字段缺了35个。
  • 缺失值所在列本身不是分析目标,且该列缺失与业务结论无关。

第一种情况最好理解。比如一份10万行的订单表,有300行缺少“运费”字段,运费在整单金额里占比又小,直接删掉这300行,分析结果几乎不会受影响。第二种情况也很常见,数据采集环节偶尔会产生“半截数据”——比如爬虫只爬到一半字段就断了,这种行留着反而干扰统计。第三种情况要小心,得结合业务判断,不能机械套用。

4.2 dropna的完整参数拆解

我经常看到有人只记得df.dropna()不带参数的默认行为,却不知道它的核心参数组合。先看默认行为:删除“任何含有缺失值”的行,即how='any'。如果你的数据有几十列,只要某一列缺了一个值,整行就没了——这往往比预期删得多得多。

参数作用示例
axis按行删(0)还是按列删(1)df.dropna(axis=1)
how'any'任一缺失即删,'all'全部缺失才删df.dropna(how='all')
thresh该行或列至少保留N个非缺失值才不删df.dropna(thresh=5)
subset只检查指定列的子集df.dropna(subset=['关键列'])
inplace是否原地修改,默认False建议一律不用,见第8节避坑

how='all'是我最常用的参数之一。它表示“只在整行全部为空时才删”——这种行几乎肯定是空数据,删了无可厚非。而how='any'则要慎用。

thresh参数更精细。它判断的是“这一行里非缺失值的数量是否达到阈值”,达到就保留,否则删除。举个例子:一个订单表有10列,thresh=6表示非空字段少于6个的行会被删除。这个参数适合处理“半截数据”,比how更灵活。我给实习生讲的时候常打个比方:这就像面试筛简历,不是看他缺哪项就淘汰,而是看满足几个关键条件,达到及格线就留下,少一项很严重但其他都好的也能进下一轮。

subset适合精细化控制:只针对某些关键列删除。比如风控建模时,“身份证号”和“手机号”是关键字段,这两个缺失的直接删,其他列缺失不参与删除判断。

4.3 删除之后别忘了检查分布

删除不是终点,删完还要确认分布没被破坏。我的例行操作是:在删除前后分别运行df["目标列"].describe(),对比样本量、均值、标准差。如果均值变化超过一个很小的容忍范围(比如2%),说明删除行为可能引入了偏差,要回头审视删除条件。

before = df["金额"].mean() df_cleaned = df.dropna(subset=["金额"]) after = df_cleaned["金额"].mean() print(f"删除前均值: {before:.2f}, 删除后均值: {after:.2f}")

这个简单的对比能帮你发现很多隐性风险。我曾经处理一份用户调研数据时,删除缺失行之后发现“年龄”均值突然涨了5岁——原因是年轻用户填问卷更随意,缺失率偏高,删除操作等于把年轻样本成批赶出去了。后来改成填充策略,问题才解决。

5. fillna()填充策略的业务逻辑:均值中位数众数、按列定制与前向/后向填充

5.1 不同数据类型的填充选择

删除是减法,填充是加法。fillna()的核心是用某个值替换缺失值,但选什么值,不是拍脑袋决定的,要分数据类型和业务含义。

数值型列的填充有三个常见选项:均值、中位数、众数(选一个代表值,或者0)。均值简单,但容易被极端值带偏——比如“薪资”列里有几个千万年薪的样本,均值可能被拉得虚高,用中位数更稳健。我一般遵循这个原则:数据近似正态分布用均值,偏态明显用中位数。这个选择背后是“让填充值尽可能贴合数据主体位置”的考量。

类别型列的处理思路不同。最常见的方案是用众数填充,即该列出现频率最高的类别。但如果缺失率较高,直接用众数会掩盖缺失本身的信息。这个时候我会退一步思考:缺失这个事实有没有业务含义?比如“用户是否订阅会员”这一列,缺失可能代表“未开通”,那与其填充,不如把缺失值显式变成“未订阅”这个类别,信息量反而更丰富。

布尔列不建议硬填True或False,尤其在做特征工程时。更好的做法是保留缺失标记,或者额外新增一列“该字段是否缺失”,把缺失本身当成一个特征。这个思路在机器学习项目中尤其重要,因为模型可以学习到“缺失模式”与目标变量之间的关系。

5.2 fillna的集中用法汇总

# 1. 用固定数值填充 df["金额"] = df["金额"].fillna(0) # 2. 用该列均值为填充 df["金额"] = df["金额"].fillna(df["金额"].mean()) # 3. 用该列中位数填充 df["金额"] = df["金额"].fillna(df["金额"].median()) # 4. 用众数填充(类别列) df["城市"] = df["城市"].fillna(df["城市"].mode()[0]) # 5. 按列名传字典,不同列用不同策略 df = df.fillna({"金额": df["金额"].median(), "城市": "未知"})

第5种用法是我日常工作里频率最高的。真实数据不会只有一列缺失,而每列的最佳填充值各不相同。传字典的方式可以一次性把不同列的填充策略都写好,代码可读性和可维护性都更高。

5.3 用groupby做分组填充:一种经常被忽略的进阶技巧

如果你手中数据有天然的分组维度,分组填充往往比全局填充精准得多。比如订单表的“金额”列有缺失,同时有“商品品类”列。不同品类的价格水平差异巨大,用全局中位数填充,会把便宜品类的缺失值填成高价,这会严重扭曲数据。正确做法是按品类分组,各组用自己的中位数填充:

df["金额"] = df.groupby("商品品类")["金额"].transform( lambda x: x.fillna(x.median()) )

这段代码的逻辑是:对每个商品品类分组,在该分组内部用该组的中位数补缺失值。transform返回和原DataFrame等长的序列,所以可以直接赋值回原列。这个技巧在电商、金融、零售领域非常实用。我强烈建议读者把groupby + transform + fillna记成一组固定操作,它比写循环处理每一类要简洁得多,性能也好得多。

5.4 时间序列场景的前向/后向填充

在时间序列数据中,缺失值往往不宜用均值或中位数填充,因为时间序列有先后顺序和惯性。例如传感器每5秒采集一次温度,某时刻数据缺失,用前一刻的温度填充比用全天平均值更合理。Pandas为此提供了前向填充(ffill)和后向填充(bfill)。

# 前向填充:用上一个有效值填充 df["温度"] = df["温度"].ffill() # 后向填充:用下一个有效值填充 df["温度"] = df["温度"].bfill()

ffill特别适合处理“采集断档”造成的缺失。比如设备每10秒上报一次数据,中间丢了几个点,用上一秒的值补上,业务上说得通。bfill一般用得少一些,但处理“数据的开头缺失”时很管用——首行缺失时ffill会留空,这时需要用bfill从后面向前补。

还有一点要提醒:如果时间序列里连续缺失很长一段,比如连续断档2小时,ffill填充出来的数据其实是“伪数据”,在趋势分析里会造成假象。这种情况更合理的做法是先判断缺失段是否超过阈值,超了就整段标记为缺失,而不是盲目填充。

6. interpolate()插值补齐:时间序列和趋势数据的最优解

6.1 线性插值的工作原理

如果缺失值所在列有明显的变化趋势,比如温度逐小时上升、销量按日波动,均值填充会破坏趋势,前后填充又会造成台阶状突变,这时interpolate()就派上用场了。它默认采用线性插值:在缺失位置前后两个有效值之间拉一条直线,按缺失点在序列中的位置按比例取值。

df["温度"] = df["温度"].interpolate()

还是用温度传感器举例:10:00测得25度,10:01缺失,10:02测得27度,线性插值会算出一个26度左右的过渡值。这个结果比前向填充的25度更接近真实状态,因为它考虑了下文信息。在曲线比较平滑的数据里,线性插值效果特别好;但如果数据本身波动剧烈,线性插值也只能是“尽力而为”。

6.2 时间索引下的插值:method='time'

线性插值默认按“行的位置”计算等距比例。如果你的DataFrame索引是时间戳,而且时间间隔不均匀——比如9:00一条数据、9:07一条、9:10一条——默认插值会假设它们等距,这会导致偏差。此时应该用method='time',让Pandas按照时间戳的实际距离计算插值权重:

df = df.set_index("时间") df["温度"] = df["温度"].interpolate(method="time")

这个细节在真实时间序列项目中非常关键。我刚开始处理传感器数据时没注意这点,用的是默认插值,后来跟精准仪器数据一对比,发现某些点偏了将近1度,排查半天才发现是不均匀时间间隔导致的。

6.3 插值的边界限制

插值也不是万能的。最典型的问题:序列开头或结尾的缺失值,插值是补不上的,因为没有两侧的有效值。我处理过一组数据,前3个小时传感器没启动,后面数据正常,用interpolate()之后发现开头的缺失依然存在——因为第一个有效值在很后面,插值无法extrapolate。这种情况我会先看缺失占比,如果开头缺失长度不长,用bfill()补上;如果太长,直接删掉前段更合理。

另外一个容易踩的坑是:interpolate()默认的limit_direction是"forward",也就是说它只对“中间夹的缺失值”做填充,不会向前或向后扩展。如果你想让首尾也被补上,需要显式指定:

df["温度"] = df["温度"].interpolate(limit_direction="both")

这个参数很多人不知道,但很多时候恰恰是首尾缺失让interpolate看起来“没生效”。填参数时顺手加上limit_direction="both",就能把边界情况一并解决。

6.4 插值法使用的几个提示

用插值法之前,建议先确认你确实需要它。均值填充适合“数据整体围绕中心波动”的场景,前向填充适合“短时断电”的场景,插值适合“数值有明显连续变化趋势”的场景。如果把插值用到类别列上,比如“城市”字段,就会填出毫无意义的中间值,那是完全错误的应用。类别列老老实实用众数或其他策略,数值列才谈得上插值。

7. 完整项目流程:缺失值处理的标准动作与收尾验证

7.1 从原始数据到处理完成的标准流程

从前面的内容可以看出,处理缺失值没有“一键方案”,靠的是分步骤的工程化决策。我把自己在项目中沉淀下来的标准流程整理出来,可以直接当模板用:

第一步,读取数据后立刻执行全面盘点,记录df.shape、df.info()、df.isna().sum()。这一步产出缺失清单。

第二步,针对每一列,判断缺失机制。我关注的三个问题:缺失是完全随机发生的,还是跟其他字段有关联?缺失比例高还是低?该字段在分析中扮演什么角色?这三个问题决定了后续策略的方向。

第三步,分组决策。缺失率极低且样本量大的,删除或填充影响都不大,按简单规则处理;缺失率中等(5%-30%)的,按列定制填充策略;缺失率超过50%且不是核心字段的,建议直接剔除该列,因为填充出来的数据可信度太低。

第四步,实施处理。核心字段用业务驱动的方式处理,非核心字段用统一规则批量处理。写代码时用fillna+ 字典,或groupby + transform的方式批量落地。

第五步,收尾验证。重新运行df.isna().sum(),确认没有遗漏;再运行df.describe(),对比处理前后核心统计量变化,防止引入偏差。

7.2 一个完整的端到端代码示例

以下是一段贴近真实项目的代码,融合了盘点、删除、分组填充和验证步骤:

import numpy as np import pandas as pd # 1. 读取并快速盘点 df = pd.read_csv("订单数据.csv") print(df.shape) print(df.isna().sum()) # 2. 缺失率计算,辅助决策 missing_ratio = df.isna().mean().sort_values(ascending=False) print(missing_ratio) # 3. 高缺失率且非核心的列,直接剔除 drop_cols = missing_ratio[missing_ratio > 0.5].index df.drop(columns=drop_cols, inplace=True) # 4. 极端“坏行”删除:非空字段不足一半则删除 threshold = df.shape[1] // 2 df = df.dropna(thresh=threshold) # 5. 按列定制填充 df["金额"] = df.groupby("商品品类")["金额"].transform(lambda x: x.fillna(x.median())) df["城市"] = df["城市"].fillna("未知") df["温度"] = df["温度"].interpolate(method="time") # 6. 验证 print(df.isna().sum()) print(df.describe())

这段代码几乎覆盖了前面所有核心知识点。需要留意的是第3步用了inplace=True,我在自己的代码里一般不这么写,下面避坑清单里会细讲原因——为了示例直观,这里先演示。

7.3 记录处理日志:容易被忽视的“最后一步”

处理缺失值时,我强烈建议把每一步操作记录在一个单独的字典或文档里。比如:“金额列缺失231条,按品类中位数填充;城市列缺失89条,用‘未知’填充”。这么做的好处有两个:第一,项目复盘时能快速回忆起当时的判断逻辑;第二,向同事或客户解释数据口径时,有据可查。数据分析项目里,数据的处理方式本身就是分析结论的一部分,缺失值处理方式的记录更是重中之重。

我见过不少分析报告因为没写清楚“缺失值如何填充”,导致结论无法复现,最后被质疑数据造假。这不是危言耸听,处理过程的可追溯性就是数据分析师的职业素养。可以用一个简单的Python字典来记录:

missing_log = { "金额": {"缺失数": 231, "处理方式": "按品类中位数填充"}, "城市": {"缺失数": 89, "处理方式": "填充为未知"}, }

8. 附赠的避坑清单:inplace、bool取反、limit方向和类型变化

8.1 别再盲目用inplace=True

Pandas很多方法都有inplace参数,比如df.dropna(inplace=True)、df.fillna(0, inplace=True)。这个参数的意思是“原地修改当前DataFrame”,听起来高效,但实际有很多隐患。最直接的问题是:Pandas计划在未来版本中移除这个参数,依赖它的代码迟早要改。其次是它在链式操作中容易产生SettingWithCopyWarning,改了却没改成,很坑。

我现在的习惯是统一不用inplace,而是写df = df.fillna(0)这种赋值式写法。这样每个操作都返回新对象,逻辑清晰,可读性好。刚开始可能觉得多打几个字母,但配合链式操作后,代码反而更简洁:

df = ( df.drop(columns=["无用列"]) .dropna(thresh=10) .fillna({"城市": "未知"}) )

8.2 isna()的结果取反要用~

Pandas的布尔数组支持~取反,比如筛选非缺失行:

# 正确写法 df[~df["金额"].isna()] # 错误写法(初学者常见) df[df["金额"].not isna()] # 语法错误 df[not df["金额"].isna()] # 报错: truth value is ambiguous

为什么not不行?因为not是Python关键字,只能作用于单个布尔值,而df["金额"].isna()返回的是一个Series。~才是逐元素取反的按位运算符。这个坑我见得太多了,尤其从R或其他语言切过来的人容易踩。

8.3 limit参数的真正作用:控制连续填充长度

fillna和ffill里都有limit参数,它的含义是“最多填充多少个连续缺失值”。打个比方,前向填充时,如果一列连续缺了10个值,limit=3就只补前3个,后面的7个仍然保持缺失。这个参数很多人不理解,以为它限制的是总填充数量,其实它限制的是“每次连续缺失的填充长度”。

df["温度"].ffill(limit=2)

这在时间序列里很实用:如果传感器连续断档超过2个点,说明问题比较严重,填充出来的数据不能相信,所以只补短断档,长断档保留缺失,后续可做特殊标记。

8.4 填充之后留意类型变化

填充操作可能改变列的数据类型。最典型的例子:整数列有缺失,填充之前是float64(因为有np.nan),填充0之后依然是float64,并不会自动变回int64。如果你需要整数类型,填充后要手动转换:

df["整数列"] = df["整数列"].fillna(0).astype(int)

如果该列原本是整数值的ID,转换成整数后,后续按ID合并数据时就不会因为1.0和1不匹配而找不到对应记录。这个类型问题虽然小,但在多表关联场景中经常成为莫名其妙的bug来源。

8.5 空字符串和空格不是缺失值

还有一个隐藏很深的坑:从Excel或CSV读入的数据,看起来是空白的单元格,读进来可能是空字符串"",而不是np.nan。isna()对空字符串返回False,但它确实没有有效内容。这个问题在手工维护的表格里尤其常见。处理方案是先看这一列有没有空字符串,有就替换成np.nan:

df["备注"] = df["备注"].replace("", np.nan)

更隐蔽的是带空格的字符串" ",这种需要用str.strip()清洗后再判断。我在一次数据清洗中发现某个“城市”列看似缺失不多,实际上里面混了不少空格值,value_counts()里出现了一个看不见的类别,差异全被它搅乱了。这类问题在真实数据里非常普遍,排查时一定要想到。

9. 我个人在实际项目里的一点体会

做了这么多年数据分析,我的感受是:处理缺失值的方法本身不难,难的是判断“为什么缺失”以及“该用哪种方式处理”。纯粹的技巧层面就是isna()、dropna()、fillna()、interpolate()这几个函数,但每个函数的参数背后,都对应着一条决策逻辑。

如果你现在正在处理一份带缺失值的数据,我建议你先别急着敲代码。花10分钟看一眼缺失清单,想一想每一列缺失背后的业务原因:是采集设备断档、是用户不愿填写、还是系统bug?原因不同,处理方案天差地别。比如“用户不愿填写收入”导致的缺失,你用均值填充就会低估高收入人群的占比,正确做法是像前面提到的“把缺失单独作为一个类别”,或者干脆建模预测缺失值。

还有一个小技巧想分享:处理缺失值时,先复制一份原始数据留作备份。df.to_csv("原始数据备份.csv", index=False)这种操作只需要一行代码,却能让你在试错过程中毫无负担。我见过太多人处理到一半发现前期删错了,想恢复却没有原始数据,只能从头再来。备份这个习惯,能省下大把时间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询