6.1 缺失值

本节前言

缺失值是数据清洗中最常见的问题。pandas 提供了完整的缺失值识别、删除、填充与插值工具。

1. 缺失值标识

pandas 中常用的缺失值标识包括:

标识类型说明
pd.NA可空类型缺失值pandas 2.x 推荐,可与多种 dtype 协同
pd.NaT时间类型缺失值用于 datetime / timedelta / period
np.nan浮点缺失值float 类型的传统缺失值
NonePython 对象通常被自动转换为 np.nan
np.inf正无穷可视为「数值异常」

注意

  • pd.NA 与 np.nan 在比较和运算中的行为不同。
  • pd.NaT 是时间戳缺失值的唯一正确表示。

2. 缺失值检测

isna() / isnull()

两个方法功能完全相同,均用于检测缺失值,返回布尔掩码。

import pandas as pd
import numpy as np
 
df = pd.DataFrame({
    'A': [1, np.nan, 3],
    'B': [np.nan, 'x', 'y'],
    'C': [pd.NaT, pd.Timestamp('2024-01-01'), pd.Timestamp('2024-01-02')]
})
 
df.isna()
#        A      B      C
# 0  False   True   True
# 1   True  False  False
# 2  False  False  False
 
df.isnull()  # 等价于 isna()
 
### notna() / notnull()
 
与 `isna()` / `isnull()` 相反,返回非缺失值掩码。
 
```python
df.notna()
df.notnull()  # 等价

使用建议

  • 现代 pandas 推荐使用 isna() / notna()。
  • 与 sum() 联用可统计每列缺失值数量:df.isna().sum()。

3. 删除缺失值:dropna()

删除包含缺失值的行或列。

参数

参数类型默认值说明
axisint / str00 或 ‘index’ 删除行;1 或 ‘columns’ 删除列
howstr’any''any’:存在即删;‘all’:全缺失才删
threshintNone至少保留的非缺失值数量
subsetlistNone只在指定列/行中检查缺失
inplaceboolFalse是否原地修改(不推荐)

示例

df = pd.DataFrame({
    'A': [1, 2, np.nan, 4],
    'B': [np.nan, 2, 3, 4],
    'C': [1, 2, 3, 4]
})
 
# 删除任意含缺失值的行
df.dropna()
#      A    B  C
# 1  2.0  2.0  2
# 3  4.0  4.0  4
 
# 全部缺失才删除
df.dropna(how='all')
 
# 至少保留 2 个非缺失值
df.dropna(thresh=2)
 
# 仅在 A、B 列检查
df.dropna(subset=['A', 'B'])
 
# 删除缺失严重的列
df.dropna(axis=1, thresh=3)

4. 填充缺失值:fillna()

参数

参数类型默认值说明
value标量 / dict / Series / DataFrameNone填充值,可为每列指定不同值
methodstrNone’ffill’ / ‘pad’:向前填充;‘bfill’ / ‘backfill’:向后填充
axisint / str0填充方向
inplaceboolFalse是否原地修改
limitintNone连续填充的最大数量
downcastdictNone填充后尝试降级 dtype

示例

s = pd.Series([1, np.nan, np.nan, 4, np.nan])
 
# 固定值填充
s.fillna(0)
 
# 每列指定不同值
df.fillna({'A': 0, 'B': 'unknown'})
 
# 向前填充(用前一个有效值)
s.fillna(method='ffill')
# 0    1.0
# 1    1.0
# 2    1.0
# 3    4.0
# 4    4.0
 
# 限制连续填充数
s.fillna(method='ffill', limit=1)
# 0    1.0
# 1    1.0
# 2    NaN
# 3    4.0
# 4    4.0

pandas 2.x 注意

pandas 2.1+ 推荐直接使用 df.ffill() / df.bfill() 替代 fillna(method=...),method 参数在部分版本中已开始弃用流程。

5. 插值:interpolate()

用插值算法估算缺失值,适用于数值型时间序列。

插值方法

方法说明
linear线性插值(默认)
time按时间间隔插值
index按索引值的比例插值
values按值插值
pad前向填充
nearest最近邻插值
zero / slinear / quadratic / cubic多项式样条插值
barycentric重心插值
polynomial多项式插值(需指定 order)
kroghKrogh 插值
piecewise_polynomial分段多项式插值
spline样条插值(需指定 order)
pchip保形三次插值
akimaAkima 插值
from_derivatives基于导数的插值

示例

s = pd.Series([1, np.nan, 3, np.nan, 5])
 
# 线性插值
s.interpolate()
# 0    1.0
# 1    2.0
# 2    3.0
# 3    4.0
# 4    5.0
 
# 时间索引插值
s_time = pd.Series(
    [1, np.nan, 3],
    index=pd.to_datetime(['2024-01-01', '2024-01-02', '2024-01-04'])
)
s_time.interpolate(method='time')
 
# 多项式插值
s.interpolate(method='polynomial', order=2)

插值适用场景

  • 时间序列的缺失值处理。
  • 平滑曲线中的间断点。
  • 当线性填充过于粗糙时使用高阶插值。

6. 前向 / 后向填充

ffill() / pad()

沿指定轴向前填充(用前一个有效值填充)。

df.ffill()          # 默认 axis=0,自上而下填充
df.ffill(axis=1)    # 横向填充
 
# pad() 是 ffill() 的别名
df.pad()

bfill() / backfill()

沿指定轴向后填充(用后一个有效值填充)。

df.bfill()
df.backfill()       # bfill() 的别名
方法别名方向
ffill()pad()前向(用前值填充)
bfill()backfill()后向(用后值填充)

7. 替换:replace()

将指定值替换为其他值。

df = pd.DataFrame({'A': [1, 999, 3], 'B': ['x', 'y', 'y']})
 
# 标量替换
df.replace(999, np.nan)
 
# 多值替换
df.replace([1, 3], [100, 300])
df.replace({1: 100, 3: 300})
 
# 按列替换
df.replace({'A': 999, 'B': 'x'}, {'A': np.nan, 'B': 'unknown'})
 
# 正则替换
df.replace(r'^x$', 'X', regex=True)

相关链接

replace() 的更多细节见 6.4 替换与裁剪。

8. 缺失值在运算中的传播

缺失值在运算中默认会「传播」,即任何含 NaN 的运算结果通常为 NaN。

s1 = pd.Series([1, 2, np.nan])
s2 = pd.Series([10, np.nan, 30])
 
s1 + s2
# 0    11.0
# 1     NaN
# 2     NaN
 
# 聚合运算自动跳过缺失值
s1.sum()       # 3.0
s1.mean()      # 1.5
s1.count()     # 2
s1.sum(skipna=False)   # nan
运算默认行为
元素级算术(+、-、*、/)NaN 传播
sum() / mean() 等聚合默认跳过 NaN(skipna=True)
比较运算与 NaN 比较结果为 False
groupby 聚合默认跳过 NaN

注意

缺失值传播是双刃剑——它保护数据完整性,但也可能在不知不觉中扩大缺失范围。务必在运算前后检查缺失值数量:df.isna().sum()。

🔗 相关笔记