Pandas按MasterID分组填充Myset列实现TRAIN/TEST均等拆分
问题根因
你之前用的GroupShuffleSplit方案不生效的核心原因有两个:
- 拆分范围仅覆盖Myset为空的子集,没有将已经标注完成的TRAIN/TEST存量记录计入平衡基数,最终全量数据的两类数量会失衡
- 该拆分逻辑是随机按组切分固定比例,不会主动优化最终总记录数的均等差值,很难达到最优平衡效果
实现逻辑
整个分配过程严格遵守两个规则:同MasterID必须同集合、全量两类记录数尽可能接近,步骤如下:
- 先统计已标注的TRAIN、TEST存量记录数,算出全量数据下两个集合各自的目标记录数(总记录数的1/2)
- 把所有Myset为空的记录按MasterID聚合,统计每个ID对应的记录条数(分配的最小单位是整个MasterID组,不能拆分单条记录)
- 打乱所有待分配ID组的顺序,用贪心策略分配:每次取一个组,分给当前离目标数量缺口更大的集合,直到所有组分配完成
- 将ID组和集合的映射关系回写到原表,填充空值
完整实现代码
import pandas as pd import random # 固定随机种子,结果可复现 random.seed(7) # 统计已标注的两类存量数量 train_cnt = len(main[main["Myset"] == "TRAIN"]) test_cnt = len(main[main["Myset"] == "TEST"]) total_records = len(main) target_num = total_records / 2 # 聚合待分配的MasterID组,统计每组记录数 unassigned_part = main[main["Myset"].isna()] group_record_count = unassigned_part.groupby("MasterID").size().to_dict() wait_assign_groups = list(group_record_count.keys()) # 打乱组顺序,消除原始排序的影响 random.shuffle(wait_assign_groups) # 贪心执行分配 group_to_set = {} for gid in wait_assign_groups: g_size = group_record_count[gid] # 优先补给缺口更大的集合 if (target_num - train_cnt) >= (target_num - test_cnt): group_to_set[gid] = "TRAIN" train_cnt += g_size else: group_to_set[gid] = "TEST" test_cnt += g_size # 回写填充原表 main["Myset"] = main.apply( lambda row: group_to_set[row["MasterID"]] if pd.isna(row["Myset"]) else row["Myset"], axis=1 )
结果说明
- 硬约束满足:所有相同MasterID的记录归属同一集合,无跨集合拆分的情况
- 平衡效果最优:最终两类记录的数量差不会超过体量最大的单个MasterID组的记录数,是规则限制下能达到的最接近均等的结果
- 基于你给出的示例数据运行,原有TRAIN共3条、TEST共1条,总记录14条单集合目标为7条,分配完成后TRAIN、TEST各7条,刚好完全均等,和你给出的预期输出逻辑一致。
内容的提问来源于stack exchange,提问作者Ash
相关产品推荐
相关产品推荐

