Pandas & NumPy Academy · 课时

填充缺失值

使用 fillna() 及其 method 参数,将 NaN 替换为常量、列均值,或使用向前填充、向后填充。

第 3 / 4 课13 个步骤

填充缺失值 是 CoddyKit 上的免费 Pandas & NumPy Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Pandas & NumPy Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Pandas & NumPy Academy 课程共包含 4 节课。

fillna() 简介

插补是指用合理的替代值替换缺失值,而不是删除所在的行。Pandas 的 fillna() 是完成此操作的主要工具:它会将 Series 或 DataFrame 中的每个 NaN 替换为您指定的值。与删除不同,插补可以保留所有行;当数据稀缺或缺失模式本身包含重要信息时,这一点尤其重要。

最简单的形式是传入一个标量:df['col'].fillna(0)。

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'product': ['A', 'B', 'C', 'D'],
    'price': [10.0, np.nan, 30.0, np.nan]
})

# Fill all NaN in 'price' with 0
filled = df['price'].fillna(0)
print(filled)
# 0    10.0
# 1     0.0
# 2    30.0
# 3     0.0

使用列均值或中位数填充

使用列的均值(适用于对称分布)或中位数(适用于偏斜分布)进行填充,是最常见的插补策略之一。这些统计量代表数据的集中趋势,因此可以尽量减少对列分布的扭曲。为避免数据泄漏,请始终在训练集分片上计算统计量。

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'salary': [50000, np.nan, 70000, 80000, np.nan, 60000]
})

mean_salary = df['salary'].mean()
median_salary = df['salary'].median()

df['salary_mean_filled'] = df['salary'].fillna(mean_salary)
df['salary_median_filled'] = df['salary'].fillna(median_salary)
print(df)
#     salary  salary_mean_filled  salary_median_filled
# 0  50000.0             50000.0               50000.0
# 1      NaN             65000.0               65000.0
# 2  70000.0             70000.0               70000.0

使用众数填充分类数据

对于分类列,使用均值或中位数没有意义。此时应使用众数,即出现频率最高的值。Series.mode()[0] 会返回最常见的值([0] 用于处理存在多个众数的情况)。

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'status': ['active', 'inactive', None, 'active', None, 'active']
})

most_common = df['status'].mode()[0]
print('Mode:', most_common)  # active

df['status'] = df['status'].fillna(most_common)
print(df['status'].tolist())
# ['active', 'inactive', 'active', 'active', 'active', 'active']

使用 ffill() 向前填充

向前填充(也称为向前携带最后一次观测值,LOCF)会将最后一个有效(非 NaN)值向后传播,以填充后续的 NaN 位置。这非常适合时间序列数据,其中测量值会保持不变,直到发生更新——例如设备状态、价格水平,或只在特定事件发生时变化的传感器读数。

import pandas as pd
import numpy as np

price = pd.Series(
    [100, np.nan, np.nan, 105, np.nan, 110],
    index=pd.date_range('2024-01', periods=6, freq='ME')
)

filled = price.ffill()
print(filled)
# 2024-01-31    100.0
# 2024-02-29    100.0
# 2024-03-31    100.0
# 2024-04-30    105.0
# 2024-05-31    105.0
# 2024-06-30    110.0

使用 bfill() 向后填充

向后填充(向后携带下一次观测值,NOCB)会将下一个有效值向前传播,以填充之前的 NaN 位置。当您知道未来数据可以补足过去的缺失值时,这种方法很有用——例如,收到月末数据后,需要回填报告到达前该月前几天的数据。

import pandas as pd
import numpy as np

df = pd.DataFrame({'value': [np.nan, np.nan, 3, np.nan, 5]})

print(df['value'].bfill())
# 0    3.0
# 1    3.0
# 2    3.0
# 3    5.0
# 4    5.0
# dtype: float64

向前和向后填充的 limit 参数

ffill() 和 bfill() 都接受 limit 参数,用于限制连续填充的 NaN 值数量。当您只希望在短暂间隔内延续某个值时,这一点非常重要;较长的间隔应保留为 NaN,以表明数据确实缺失,而不只是延迟到达。

import pandas as pd
import numpy as np

s = pd.Series([1.0, np.nan, np.nan, np.nan, 5.0])

# Fill only the first NaN gap, leave the rest
print(s.ffill(limit=1))
# 0    1.0
# 1    1.0   <- filled
# 2    NaN   <- still NaN (limit reached)
# 3    NaN
# 4    5.0

对不同列采用不同的填充方式

在实际的 DataFrame 中,不同列可能需要不同的填充策略。向 fillna() 传入一个字典,其中键是列名,值是填充值。这样一次调用就能对各列分别进行插补,比连续多次单独调用 fillna 更清晰。

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'age': [25, np.nan, 30],
    'income': [50000, np.nan, 70000],
    'country': ['USA', np.nan, 'UK']
})

fill_values = {
    'age': df['age'].median(),
    'income': df['income'].mean(),
    'country': 'Unknown'
}

filled = df.fillna(fill_values)
print(filled)
#     age   income  country
# 0  25.0  50000.0      USA
# 1  27.5  60000.0  Unknown
# 2  30.0  70000.0       UK

分组感知插补

一种更高级的策略是使用组均值或中位数填充 NaN,而不是使用整列均值。例如,可以使用相应职位类别的工资中位数来填充缺失的工资。这种方法能够保留子群体结构,比使用全局统计量得到更符合实际的插补结果。

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'dept': ['Eng', 'Eng', 'HR', 'HR', 'Eng'],
    'salary': [90000, np.nan, 50000, np.nan, 110000]
})

# Fill salary NaN with the median salary for each department
df['salary'] = df.groupby('dept')['salary'].transform(
    lambda x: x.fillna(x.median())
)
print(df)
#    dept   salary
# 0   Eng  90000.0
# 1   Eng  100000.0   <- group median (90k+110k)/2
# 2    HR  50000.0
# 3    HR  50000.0
# 4   Eng  110000.0

使用常量哨兵值填充

有时,合适的插补值是用于表示“未知”的哨兵值,而不是真实测量值。例如,用 -1 表示未知年龄,用 'N/A' 表示未知类别,或用 False 表示未知的布尔标志。只有在后续代码会明确检查这些哨兵值,并将其与真实数据区别对待时,使用哨兵值才是合理的。

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'user_id': [1, 2, 3],
    'referral_source': ['email', np.nan, 'ad'],
    'trial_days': [14, np.nan, 7]
})

df['referral_source'] = df['referral_source'].fillna('unknown')
df['trial_days'] = df['trial_days'].fillna(-1).astype(int)
print(df)
#    user_id referral_source  trial_days
# 0        1           email          14
# 1        2         unknown          -1
# 2        3              ad           7

在管道中串联 fillna()

与所有 Pandas 方法一样,fillna() 会返回一个新的 DataFrame,并且可以很好地融入方法链。在 dropna() 后调用 .fillna() 可以应用两步策略:先删除缺少关键列的行,再用合理的默认值填充剩余的可选 NaN 值——整个过程都在一个易读的管道中完成。

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'user_id': [1, None, 3, 4],
    'score': [88, 95, np.nan, 76],
    'label': ['A', 'B', None, 'D']
})

cleaned = (
    df
    .dropna(subset=['user_id'])
    .fillna({'score': df['score'].mean(), 'label': 'Unknown'})
)
print(cleaned)
#    user_id  score    label
# 0      1.0   88.0        A
# 2      3.0   86.3  Unknown
# 3      4.0   76.0        D

验证不再存在 NaN

完成插补后,请始终验证目标列中是否还存在 NaN 值。可以调用 df.isna().sum(),或断言计数为零。意外的非零计数表示 fillna 遗漏了某种情况——可能是某列的数据类型阻止了填充,也可能是您忘记在字典中加入某一列。

import pandas as pd
import numpy as np

df = pd.DataFrame({'a': [1.0, np.nan, 3.0], 'b': [np.nan, 2.0, 3.0]})
filled = df.fillna(0)

# Verify
assert filled.isna().sum().sum() == 0, 'Some NaN remain!'
print('All NaN filled successfully')
print(filled.isna().sum())
# a    0
# b    0
# dtype: int64

快速检查

检验您对 Pandas 中填充缺失值的理解。

课程回顾

本课您学到了:fillna(scalar) 会用常量替换所有 NaN,fillna(mean/median) 会使用列统计量进行插补,而 ffill()/bfill() 会为时间序列传播相邻值。向 fillna() 传入字典可以为不同列指定不同策略,使用 groupby().transform() 可以进行分组感知插补。接下来,我们将介绍插值,以及何时应选择高级插补技术。

免费开始

用 AI 导师学习 Python — 免费

在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。

课程
30
课程
120

常见问题解答

「填充缺失值」课时是免费的吗?

是的 — 「填充缺失值」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Pandas & NumPy Academy 课程的其余内容,请升级到 CoddyKit PRO。 Pandas & NumPy Academy 课程共包含 4 节课。

「填充缺失值」这节课中我会学到什么?

使用 fillna() 及其 method 参数,将 NaN 替换为常量、列均值,或使用向前填充、向后填充。 你通过在浏览器中直接运行的动手代码来练习 Pandas & NumPy Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Pandas & NumPy Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Pandas & NumPy Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「填充缺失值」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Pandas & NumPy Academy 课中编写并运行代码吗?

能。每节 Pandas & NumPy Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 检测缺失值
  2. 删除缺失值
  3. 填充缺失值
  4. 插值与高级缺失值填补
← 返回 Pandas & NumPy Academy