
本文介绍一种高效、稳健的 Pandas 方法:针对 area 列中的缺失值(NaN),按 total 列分组计算非空 area 的中位数,并利用 merge_asof 实现“向下就近匹配”——即当某 total 值无对应中位数时,自动回退到小于等于该值的最大可用 total 组的中位数进行填充。
本文介绍一种高效、稳健的 pandas 方法:针对 `area` 列中的缺失值(nan),按 `total` 列分组计算非空 `area` 的中位数,并利用 `merge_asof` 实现“向下就近匹配”——即当某 `total` 值无对应中位数时,自动回退到小于等于该值的最大可用 `total` 组的中位数进行填充。
在实际数据分析中,常需根据业务逻辑对缺失值进行智能填充。本例要求:对 area 列的 NaN 值,优先使用相同 total 值对应的所有非空 area 的中位数;若该 total 下无有效 area 数据,则降序查找最接近且 ≤ 当前 total 的、存在中位数的 total 分组,并复用其 area 中位数。这本质上是一种“有序键的向后填充式映射”,pandas.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]
})
# Step 1: 按 total 分组,计算每组 area 的中位数(自动忽略 NaN)
# 并剔除所有中位数为 NaN 的组(即该 total 下无有效 area)
tmp = df.groupby('total')['area'].median().dropna()
# Step 2: 使用 merge_asof 进行“向后最近匹配”
# 注意:merge_asof 要求左右键均升序;tmp 的 index 即 total,已天然有序
# 将原始 df 的 total 提取为独立 DataFrame(带原始索引),与 tmp 合并
filled_series = pd.merge_asof(
df[['total']].reset_index(), # 左表:含原始索引和 total
tmp.reset_index(name='area_median'), # 右表:total → area_median
on='total'
).set_index('index')['area_median']
# Step 3: 仅对原 df 中 area 为 NaN 的位置,用 filled_series 对应值填充
df.loc[df['area'].isna(), 'area'] = filled_series[df['area'].isna()]运行后,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
✅ 关键说明:
- groupby('total')['area'].median() 自动跳过 NaN,返回每个 total 对应的中位数(如 total=8 → (51+53)/2 = 52);
- dropna() 确保右表 tmp 仅含有效中位数,避免 merge_asof 匹配到无效值;
- merge_asof(..., on='total') 默认 direction='backward',即对左表每个 total,查找右表中 ≤ 它且最接近的 total 键 —— 完美契合“降序回退找相似 total”的需求(如 total=59 匹配 total=56,total=20 匹配 total=8);
- 使用布尔索引 df['area'].isna() 精准定位待填充位置,避免覆盖已有有效值。
⚠️ 注意事项:
- merge_asof 要求 on 列在左右表中均严格升序;若原始 total 无序,需先 sort_values();
- 若所有 total ≤ 当前值 的组均无有效 area(即 tmp 为空),merge_asof 将返回 NaN,建议增加兜底逻辑(如全局中位数);
- 此方法时间复杂度为 O(n log m),远优于循环逐行查找,适合中大型数据集。
该方案兼顾准确性、可读性与性能,是处理“基于邻近分组统计量填充缺失值”问题的标准实践。

















