本文介绍一种高效、稳健的 Pandas 方法:针对 area 列中的缺失值(NaN),按最接近且不大于当前行 total 值的分组中位数进行填充,避免循环与重复计算,适用于大规模数据场景。
本文介绍一种高效、稳健的 pandas 方法:针对 `area` 列中的缺失值(nan),按最接近且不大于当前行 `total` 值的分组中位数进行填充,避免循环与重复计算,适用于大规模数据场景。
在实际数据分析中,常需基于业务逻辑对缺失值进行智能填充——而非简单使用全局均值或前向填充。本例要求:对 area 列的每个 NaN,查找 total 值相等或略小(向下匹配)的非空 area 数据,取其中位数作为填充依据。例如:total=59 无对应 area,则回退至 total=56(最近的、≤59 的存在 area 的值),取其 area=34 填充;total=20 无对应 area,但 total=8 有多个 area(51, 53),中位数为 52,故填入 52。
直接遍历 + 递减搜索(如原函数 find_area)效率低、易出错,且未处理边界情况(如 total 递减至最小值仍无匹配)。推荐采用 merge_asof + 分组中位数 的向量化方案,兼具可读性与性能:
import pandas as pd
import numpy as np
# 构造示例数据
df = pd.DataFrame({
'total': [5, 8, 8, 8, 20, 56, 59],
'area': [40, 51, 53, np.nan, np.nan, 34, np.nan]
})
# 步骤 1:按 total 分组,计算 area 中位数,并剔除全 NaN 组
median_map = df.groupby('total')['area'].median().dropna()
# 步骤 2:准备左表(含索引,用于后续定位)
left = df[['total']].reset_index()
# 步骤 3:merge_asof 实现「向下就近匹配」
# 注意:需确保 median_map 的索引(即 total)已排序,且 left 按 total 升序排列
merged = pd.merge_asof(
left.sort_values('total'),
median_map.sort_index().rename('area_fill'),
on='total',
allow_exact_matches=True # 允许 total 相等时精确匹配
).set_index('index')
# 步骤 4:仅对 area 为 NaN 的位置,用匹配到的中位数填充
mask = df['area'].isna()
df.loc[mask, 'area'] = merged.loc[mask, 'area_fill'].values
print(df)
输出结果:
total area 0 5 40.0 1 8 51.0 2 8 53.0 3 8 52.0 4 20 52.0 5 56 34.0 6 59 34.0
✅ 关键要点说明:
- merge_asof 默认执行「向后最近匹配」(direction='backward'),即对每个 total 查找 ≤ 它的最大可用 total 键——完美契合“不大于当前 total”的业务需求;
- dropna() 确保只保留有有效 area 的 total 分组,避免引入 NaN 干扰匹配;
- 使用 reset_index() + set_index('index') 保持原始行序,确保填充位置准确;
- 若数据中 total 存在重复且需严格升序,务必提前 sort_values,否则 merge_asof 行为不可控。
⚠️ 注意事项:
- 若所有 total 均小于最小非空组(如 total=3 且最小有效 total=5),merge_asof 将返回 NaN,此时需额外处理(如填充全局中位数或报错);
- merge_asof 要求左右键列均为数值型且已排序,字符串或时间类型需先转换;
- 该方法天然支持多列扩展(如按 category 和 total 双维度分组),只需调整 groupby 和 merge_asof 的键即可。
此方案摒弃了低效循环,充分利用 Pandas 向量化操作与有序合并能力,是工业级缺失值填充的推荐实践。











