6.1 缺失值
本节前言
缺失值是数据清洗中最常见的问题。pandas 提供了完整的缺失值识别、删除、填充与插值工具。
1. 缺失值标识
pandas 中常用的缺失值标识包括:
| 标识 | 类型 | 说明 |
|---|---|---|
pd.NA | 可空类型缺失值 | pandas 2.x 推荐,可与多种 dtype 协同 |
pd.NaT | 时间类型缺失值 | 用于 datetime / timedelta / period |
np.nan | 浮点缺失值 | float 类型的传统缺失值 |
None | Python 对象 | 通常被自动转换为 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()
删除包含缺失值的行或列。
参数
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
axis | int / str | 0 | 0 或 ‘index’ 删除行;1 或 ‘columns’ 删除列 |
how | str | ’any' | 'any’:存在即删;‘all’:全缺失才删 |
thresh | int | None | 至少保留的非缺失值数量 |
subset | list | None | 只在指定列/行中检查缺失 |
inplace | bool | False | 是否原地修改(不推荐) |
示例
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 / DataFrame | None | 填充值,可为每列指定不同值 |
method | str | None | ’ffill’ / ‘pad’:向前填充;‘bfill’ / ‘backfill’:向后填充 |
axis | int / str | 0 | 填充方向 |
inplace | bool | False | 是否原地修改 |
limit | int | None | 连续填充的最大数量 |
downcast | dict | None | 填充后尝试降级 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.0pandas 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) |
krogh | Krogh 插值 |
piecewise_polynomial | 分段多项式插值 |
spline | 样条插值(需指定 order) |
pchip | 保形三次插值 |
akima | Akima 插值 |
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()。