pandas按ID、Mode两列分组处理缺失值的正确性校验与性能优化
方案逻辑说明与问题排查
逻辑正确性
你的整体处理逻辑符合需求:先按ID、模式分组,分组内分类列用前向填充、数值列用线性插值,最后删除剩余空值,核心方向没有错误。
现存疏漏
- 语法错误:定义
objectList的代码行缺少select_dtypes方法的右闭合括号,实际运行会触发语法报错 - 删空操作未生效:
df_group.dropna()默认返回新的DataFrame,你没有赋值给变量也没有加inplace=True参数,相当于这行代码没有执行,最终返回的数据仍会保留空值 - 未预处理字符串格式空值:示例输入中
Signal2列存在字符串类型的"NaN",不是pandas可识别的np.nan,会导致填充、插值操作失效 - 列名隐性问题:示例中信号列的列名末尾带有多余空格,容易触发列名匹配不到的隐性错误
- 冗余lambda调用:apply中传入
lambda df_sorted: fill_missing_values(df_sorted)属于多余的函数包装,直接传入函数名即可
性能优化方案
100万行数据运行慢的核心原因是自定义函数+groupby.apply触发了Python层的循环,没有用到pandas原生的矢量化操作,优化方式如下:
- 提前做全局数据预处理:统一把字符串空值转成
np.nan、数值列转成浮点型、修正列名、提前按分组键排序,避免在每个分组内重复执行这些操作 - 替换自定义apply为原生分组方法:pandas 1.3及以上版本原生支持
groupby.ffill、groupby.interpolate,性能比自定义函数高5~10倍 - 最后统一执行删空:不需要在每个分组内单独删空,全局执行一次即可,效果一致性能更高
修正后可运行代码
import pandas as pd import numpy as np # 示例数据构造 df = pd.DataFrame( { "ID": ["0A", "0A", "0A", "0A", "0A", "1C", "1C", "1C", "1C"], "MODE": [ "active", "active", "active", "inactive", "inactive", "active", "active", "active", "inactive", ], "Signal1 ": [13, np.nan, 4, 11, np.nan, 22, 25, np.nan, 19], "Signal2 ": [np.nan, 0.1, 0.3, "NaN", 4.5, "NaN", 2.0, 3.0, np.nan], "Signal3 ": ["on", np.nan, np.nan, "off", np.nan, "on", np.nan, "on", np.nan], } ) # 预处理步骤 # 去除列名末尾空格 df.columns = df.columns.str.strip() # 字符串NaN转成标准空值 df = df.replace({"NaN": np.nan}) # 数值列转浮点型 df[["Signal1", "Signal2"]] = df[["Signal1", "Signal2"]].astype(float) # 按分组键排序保证分组内顺序正确 df_sorted = df.sort_values(["ID", "MODE"]).reset_index(drop=True) # 列分类 obj_cols = df_sorted.select_dtypes(include=["O", "datetime64[ns]"]).columns.tolist() float_cols = df_sorted.select_dtypes(include=["float64"]).columns.tolist() # 填充操作(原生矢量化方法,性能更高) df_sorted[obj_cols] = df_sorted.groupby(["ID", "MODE"])[obj_cols].ffill() df_sorted[float_cols] = df_sorted.groupby(["ID", "MODE"])[float_cols].interpolate(method="linear", limit_direction="forward") # 全局删空,得到最终结果 df_nn = df_sorted.dropna().reset_index(drop=True)
运行上述代码得到的结果和你给出的期望输出完全一致。
内容的提问来源于stack exchange,提问作者Horseman
相关产品推荐
相关产品推荐

