缺失值是数据清洗中最常见的问题。pandas 提供了完整的缺失值表示、检测、删除、填充与插值体系。


1. 缺失值类型

在 pandas 中,缺失值有多种表示方式,需根据数据类型选用。

标识类型说明适用场景
pd.NApandas 通用缺失值可空类型的默认缺失值Int64、string、boolean 等扩展类型
pd.NaT时间缺失值缺失的时间戳/时间差datetime64、timedelta64、period
np.nan浮点缺失值最常见的缺失值,属于 float所有数值类型(默认)
NonePython 对象在 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     True

notna() / 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.0

DataFrame 按列填充

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