缺失值是数据清洗中最常见的问题。pandas 提供了完整的缺失值表示、检测、删除、填充与插值体系。
1. 缺失值类型
在 pandas 中,缺失值有多种表示方式,需根据数据类型选用。
| 标识 | 类型 | 说明 | 适用场景 |
|---|---|---|---|
pd.NA | pandas 通用缺失值 | 可空类型的默认缺失值 | Int64、string、boolean 等扩展类型 |
pd.NaT | 时间缺失值 | 缺失的时间戳/时间差 | datetime64、timedelta64、period |
np.nan | 浮点缺失值 | 最常见的缺失值,属于 float | 所有数值类型(默认) |
None | Python 对象 | 在 object 类型中表示缺失 | 混合类型列 |
Inf / -Inf | 无穷大 | 数学上的无穷,可视为异常值 | 数值计算 |
import pandas as pd
import numpy as np
# 不同类型的缺失
s1 = pd.Series([1, None, 3], dtype='Int64') # Int64 -> pd.NA
s2 = pd.Series([1.0, np.nan, 3.0]) # float -> np.nan
s3 = pd.Series(pd.date_range('2024-01-01', periods=3).insert(1, pd.NaT)) # datetime -> NaT
print(s1)
# 0 1
# 1 <NA>
# 2 3
# dtype: Int64兼容性提醒
np.nan是浮点数,在整数列中会自动转为float64- 若希望保留整数列并支持缺失值,应使用可空整数类型
Int64(大写开头)None在object列中存储,转换为数值列时会变为nan或pd.NA
2. 缺失值检测
isna() / isnull()
isna() 与 isnull() 完全等价,用于检测缺失值,返回布尔型对象(与输入同形状)。
df = pd.DataFrame({
'A': [1, 2, None, 4],
'B': [5, None, 7, 8]
})
df.isna()
# A B
# 0 False False
# 1 False True
# 2 True False
# 3 False False
# Series 检测
s = pd.Series([1, np.nan, None, pd.NA])
s.isna()
# 0 False
# 1 True
# 2 True
# 3 Truenotna() / notnull()
notna() / notnull() 是 isna() / isnull() 的反操作。
df.notna()
# A B
# 0 True True
# 1 True False
# 2 False True
# 3 True True判断单个值
使用
pd.isna(value)或pd.notna(value),可同时处理NaN、None、NaT、pd.NA。
统计缺失值数量
# 每列缺失数量
df.isna().sum()
# A 1
# B 1
# 总缺失数量
df.isna().sum().sum()
# 存在缺失的列
df.columns[df.isna().any()]
# 无缺失的列
df.columns[df.notna().all()]
# 每行缺失数量
df.isna().sum(axis=1)3. 删除缺失值:dropna()
dropna() 用于删除包含缺失值的行或列。
参数详解
| 参数 | 说明 |
|---|---|
axis | 删除轴:0 或 'index'(删除行,默认),1 或 'columns'(删除列) |
how | 删除条件:'any'(任一缺失即删除,默认),'all'(全部缺失才删除) |
thresh | 非缺失值数量阈值:行/列中至少要有 thresh 个非缺失值才保留 |
subset | 指定在哪些列/行中检查缺失值(数组) |
inplace | 是否原地修改(默认 False) |
示例
df = pd.DataFrame({
'A': [1, 2, np.nan, 4],
'B': [5, np.nan, 7, 8],
'C': [np.nan, np.nan, np.nan, 10]
})
# 删除包含任意缺失值的行(默认)
df.dropna()
# A B C
# 3 4.0 8.0 10.0
# 删除全部为缺失值的行
df.dropna(how='all')
# A B C
# 0 1.0 5.0 NaN
# 1 2.0 NaN NaN
# 2 NaN 7.0 NaN
# 3 4.0 8.0 10.0
# 删除含有缺失值的列
df.dropna(axis=1)
# A B
# 0 1.0 5.0
# 1 2.0 NaN
# 2 NaN 7.0
# 3 4.0 8.0
# 指定检查列
df.dropna(subset=['A', 'B'])
# A B C
# 0 1.0 5.0 NaN
# 3 4.0 8.0 10.0
# 至少要有 2 个非缺失值才保留行
df.dropna(thresh=2)
# A B C
# 0 1.0 5.0 NaN
# 1 2.0 NaN NaN
# 2 NaN 7.0 NaN
# 3 4.0 8.0 10.0
# 原地删除
df.dropna(inplace=True)注意
dropna()不会自动重置索引,删除后索引可能不连续。如需重置可使用reset_index(drop=True)。- 在时间序列中,通常使用
dropna(subset=[关键列])而不是删除所有含缺失值的行。
4. 填充缺失值:fillna()
fillna() 使用指定值或方法填充缺失值。
参数详解
| 参数 | 说明 |
|---|---|
value | 填充值:标量、Series、DataFrame 或字典(按列指定) |
method | 填充方法:None、'ffill'(前向填充)、'bfill'(后向填充) |
axis | 填充轴:0 或 'index'(默认,沿行向下填充),1 或 'columns'(沿列填充) |
inplace | 是否原地修改 |
limit | 最大填充连续缺失值数量 |
downcast | 填充后是否尝试降级类型(如 'infer') |
示例
s = pd.Series([1, np.nan, np.nan, 4])
# 标量填充
s.fillna(0)
# 0 1.0
# 1 0.0
# 2 0.0
# 3 4.0
# 前向填充(用前一个非缺失值填充)
s.fillna(method='ffill')
# 0 1.0
# 1 1.0
# 2 1.0
# 3 4.0
# 后向填充(用后一个非缺失值填充)
s.fillna(method='bfill')
# 0 1.0
# 1 4.0
# 2 4.0
# 3 4.0
# 限制连续填充次数
s.fillna(method='ffill', limit=1)
# 0 1.0
# 1 1.0
# 2 NaN
# 3 4.0DataFrame 按列填充
df = pd.DataFrame({
'A': [1, np.nan, 3],
'B': [np.nan, 2, np.nan]
})
# 统一填充
df.fillna(0)
# A B
# 0 1.0 0.0
# 1 0.0 2.0
# 2 3.0 0.0
# 按列指定填充值
df.fillna({'A': -1, 'B': -99})
# A B
# 0 1.0 -99.0
# 1 -1.0 2.0
# 2 3.0 -99.0
# 使用 Series/DataFrame 填充
fill_series = pd.Series({'A': 10, 'B': 20})
df.fillna(fill_series)
# 前向填充(垂直方向)
df.fillna(method='ffill')常见填充策略
| 场景 | 推荐方法 |
|---|---|
| 时间序列缺口 | fillna(method='ffill') 或 interpolate() |
| 数值列均值填充 | df.fillna(df.mean()) |
| 分类列众数填充 | df.fillna(df.mode().iloc[0]) |
| 按组填充 | df.groupby('组')['值'].transform(lambda x: x.fillna(x.mean())) |
5. 前向/后向填充:ffill() / pad()、bfill() / backfill()
ffill()(pad() 别名)用前一个非缺失值填充;bfill()(backfill() 别名)用后一个非缺失值填充。
参数
| 参数 | 说明 |
|---|---|
axis | 填充轴:0 或 'index'(默认),1 或 'columns' |
inplace | 是否原地修改 |
limit | 最大连续填充次数 |
示例
s = pd.Series([1, np.nan, 2, np.nan, np.nan, 6])
# 前向填充
s.ffill()
# 0 1.0
# 1 1.0
# 2 2.0
# 3 2.0
# 4 2.0
# 5 6.0
# 后向填充
s.bfill()
# 0 1.0
# 1 2.0
# 2 2.0
# 3 6.0
# 4 6.0
# 5 6.0
# 沿列填充
df.ffill(axis=1)时间序列的应用
- 金融数据中,缺失的价格通常用前值填充(
ffill)- 传感器数据可用后值填充(
bfill)- 更平滑的处理可使用
interpolate()
6. 插值填充:interpolate()
interpolate() 通过插值算法估算缺失值,适用于数值型时间序列或连续数据。
参数详解
| 参数 | 说明 |
|---|---|
method | 插值方法(见下方列表) |
axis | 插值轴:0 = 按行,1 = 按列 |
limit | 最大连续缺失值插值数量 |
limit_direction | 插值方向:'forward'、'backward'、'both' |
limit_area | 插值区域:None、'inside'(只填内部)、'outside'(只填边缘) |
inplace | 是否原地修改 |
downcast | 类型降级 |
**kwargs | 传给指定插值方法的额外参数(如 order、spline_order 等) |
插值方法列表
| method | 说明 |
|---|---|
'linear' | 线性插值(默认),按索引均匀间隔 |
'time' | 基于时间索引的线性插值 |
'index' | 基于索引值的线性插值 |
'values' | 基于底层数值的插值 |
'pad' | 前向填充(等同于 ffill) |
'nearest' | 最近邻插值 |
'zero' | 零阶插值(阶梯状) |
'slinear' | 一阶样条(同线性) |
'quadratic' | 二阶样条插值 |
'cubic' | 三阶样条插值 |
'barycentric' | 重心插值 |
'polynomial' | 多项式插值(需指定 order) |
'krogh' | Krogh 插值(高阶多项式) |
'piecewise_polynomial' | 分段多项式插值 |
'spline' | 样条插值(需指定 order) |
'pchip' | 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.interpolate(method='polynomial', order=2)
# 时间索引插值
ts = pd.Series([1, np.nan, 3], index=pd.to_datetime(['2024-01-01', '2024-01-10', '2024-01-20']))
ts.interpolate(method='time')
# 2024-01-01 1.0
# 2024-01-10 2.0
# 2024-01-20 3.0
# 限定填充方向
s.interpolate(limit_direction='backward')注意事项
- 多项式/样条插值需要
scipy库支持(pip install scipy)- 插值假设数据是连续的,对于分类数据不适用
- 在时间序列中,
method='time'会根据时间间隔进行插值,比linear更准确
7. 替换:replace()
replace() 将指定值替换为其他值,可处理精确匹配、正则表达式、列表、字典等多种形式。
基本用法
s = pd.Series([1, 2, 3, 4])
# 单值替换
s.replace(2, 20)
# 0 1
# 1 20
# 2 3
# 3 4
# 多值替换(列表)
s.replace([1, 2], [100, 200])
# 0 100
# 1 200
# 2 3
# 3 4
# 字典替换
df = pd.DataFrame({'A': ['yes', 'no', 'yes'], 'B': ['是', '否', '是']})
df.replace({'yes': 1, 'no': 0, '是': True, '否': False})
# 缺失值替换(用 np.nan 等)
s.replace(0, np.nan)参数详解
| 参数 | 说明 |
|---|---|
to_replace | 被替换的值:标量、列表、字典、正则表达式、None 等 |
value | 替换后的值(当 to_replace 为字典时可省略) |
inplace | 是否原地修改 |
limit | 最大替换次数 |
regex | 是否将 to_replace 解释为正则表达式 |
method | 替换方法:'pad'/'ffill'、'bfill'(当 to_replace 为标量时使用前后值填充) |
正则替换
s = pd.Series(['foo123', 'bar456', 'baz'])
# 用正则替换
s.replace(r'\d+', 'NUM', regex=True)
# 0 fooNUM
# 1 barNUM
# 2 baz与 fillna 结合
# 将多种缺失标记统一为 np.nan
df = pd.DataFrame({'A': ['NULL', 'N/A', '1', '2']})
df.replace(['NULL', 'N/A'], np.nan)
# 将缺失值替换为特定值
df.fillna(0)缺失值替换优先级
清洗顺序建议:先
replace统一缺失标记 → 再dropna/fillna处理缺失。
8. 缺失值在运算中的传播
pandas 的算术运算遵循缺失值传播规则:只要参与运算的任一位置缺失,结果即为缺失。
s1 = pd.Series([1, 2, np.nan])
s2 = pd.Series([10, np.nan, 30])
s1 + s2
# 0 11.0
# 1 NaN
# 2 NaN跳过缺失值的聚合
s = pd.Series([1, 2, np.nan, 4])
s.sum() # 7.0(跳过 NaN)
s.mean() # 2.333(跳过 NaN)
s.describe() # 自动忽略 NaN
# 显式控制 skipna
s.sum(skipna=False) # NaN(不跳过)比较运算中的缺失值
s = pd.Series([1, np.nan, 3])
s > 2
# 0 False
# 1 False
# 2 True
# dtype: bool注意
缺失值参与比较时,结果一律视为
False(包括s > 2中的NaN)。若需要保留缺失状态,可使用s.gt(2)并指定fill_value,但默认仍为False。
与 groupby 等操作结合
df = pd.DataFrame({
'组': ['A', 'A', 'B', 'B'],
'值': [1, np.nan, 3, 4]
})
df.groupby('组')['值'].sum()
# 组
# A 1.0
# B 7.0小结
- 检测:
isna()/notna()全类型通用- 删除:
dropna()支持行/列、how、thresh、subset- 填充:
fillna()支持标量/字典/对象,ffill/bfill适合序列- 插值:
interpolate()提供从线性到样条的多种方法- 替换:
replace()可统一缺失标记、正则替换- 传播:缺失值在运算与聚合中默认被跳过,但比较运算返回
False