
本文介绍在 Pandas 中根据某列(如 model_version)对 DataFrame 分组,并为每组调用对应预训练模型完成预测的高效方法,避免手动切片和重复代码,实现真正的“一次执行、自动分发”。
本文介绍在 pandas 中根据某列(如 `model_version`)对 dataframe 分组,并为每组调用对应预训练模型完成预测的高效方法,避免手动切片和重复代码,实现真正的“一次执行、自动分发”。
在实际机器学习工程中,常需对同一数据集的不同子集应用不同版本或类型的模型(例如 A/B 测试、灰度发布、多策略路由等)。若按传统方式先用 query() 或布尔索引拆分数据、再分别预测,不仅冗余,还难以维护和扩展。更优雅的解法是利用 Pandas 的 groupby().apply() 机制,结合模型字典实现声明式模型路由。
核心思路是:将模型对象预先存入字典(键为分组依据的值),再通过 groupby('model_version') 对 DataFrame 分组,最后在 apply 中根据 group.name(即当前组的分组键)查表获取对应模型并调用 .predict()。
以下为完整可运行示例:
import pandas as pd
import pickle
# 构造示例数据
df = pd.DataFrame({
'id': [1, 2],
'var1': [5, 6],
'var2': [20, 60],
'var3': [8, -2],
'model_version': ['model_a', 'model_b']
})
# 假设已加载模型(实际路径请替换)
# with open('model_a.pkl', 'rb') as f: model_a = pickle.load(f)
# with open('model_b.pkl', 'rb') as f: model_b = pickle.load(f)
# 此处用占位函数模拟模型行为
class MockModel:
def predict(self, X): return [X['var1'].iloc[0] * 2 + X['var2'].iloc[0]]
model_a, model_b = MockModel(), MockModel()
# ✅ 推荐方案:单次 groupby + apply,返回 Series(索引对齐原 df 分组)
models = {'model_a': model_a, 'model_b': model_b}
predictions = df.groupby('model_version').apply(lambda g: models[g.name].predict(g))
print(predictions)
# 输出类似:
# model_version
# model_a [30]
# model_b [72]
# dtype: object
⚠️ 注意事项:
groupby().apply()默认将分组结果拼接为一个 Series,其索引为分组键(如'model_a','model_b'),不保留原始行索引。若需与原df行一一对应(例如追加为新列),推荐使用字典推导式 +pd.concat():# 返回字典:{group_key: prediction_array} preds_dict = {k: models[k].predict(g) for k, g in df.groupby('model_version')} # 合并为与原 df 索引对齐的 Series(需确保 predict 返回一维数组/列表) all_preds = pd.concat([ pd.Series(pred, index=g.index) for (k, g), pred in zip(df.groupby('model_version'), preds_dict.values()) ]) df['prediction'] = all_preds # 成功添加预测列
- 模型必须支持接收
pd.DataFrame(不含目标列)作为输入;若模型仅接受numpy.ndarray,建议在predict()前统一调用.values(如models[g.name].predict(g[['var1','var2','var3']].values))。- 若分组依据是
id列(如题中 NB 提示),只需同步更新模型字典键(如{1: model_a, 2: model_b})并改用groupby('id')即可,逻辑完全一致。
总结:通过 groupby().apply() + 模型字典映射,我们以声明式、可扩展的方式实现了“一行代码驱动多模型预测”,既保持了代码简洁性,又为后续接入更多模型版本预留了清晰接口。










