本文介绍如何在 pandas 中仅对 dataframe 中相同分组内的行进行随机重排,同时严格保持各组在原始数据中的出现顺序(即组间顺序不变),适用于去重后需保留结构、训练集构造或数据增强等场景。
本文介绍如何在 pandas 中仅对 dataframe 中相同分组内的行进行随机重排,同时严格保持各组在原始数据中的出现顺序(即组间顺序不变),适用于去重后需保留结构、训练集构造或数据增强等场景。
在数据分析和机器学习预处理中,常需对具有相同标签或分类的样本进行内部随机化(如打乱同一类别下的样本顺序),但又不能破坏不同类别在原始数据中的先后关系——例如,确保所有 sum=4 的行仍整体位于 sum=3 之后、sum=5 之前。df.sample(frac=1) 全局打乱显然不满足该需求;正确解法是按组采样 + 保持组序。
✅ 推荐方案:groupby(..., sort=False).sample()
当分组键(如 'sum')在原始 DataFrame 中已自然连续(即相同值的行相邻),且你希望输出中组块顺序与原始一致(如 3 → 4 → 5),只需使用:
import pandas as pd
data = {'Org': ['Tom', 'Kelly', 'Rick', 'Dave', 'Sara', 'Liz'],
'sum': [3, 4, 4, 4, 5, 5]}
df = pd.DataFrame(data)
# 按 'sum' 分组,禁用自动排序,对每组内行完全随机采样(frac=1 即全量)
shuffled = df.groupby('sum', sort=False).sample(frac=1, random_state=42).reset_index(drop=True)
print(shuffled)
✅ 输出示例(random_state 保证可复现):
Org sum 0 Tom 3 1 Dave 4 2 Rick 4 3 Kelly 4 4 Sara 5 5 Liz 5
? 关键点:sort=False 防止 groupby 自动按分组键升序重排组块;sample(frac=1) 在每组内执行无放回全量随机重排;.reset_index(drop=True) 清理被打乱的索引。
⚠️ 进阶场景:组内行物理位置分散时,仍需保组块“原位”
若相同分组值的行在原始数据中不连续(例如 sum=4 出现在第1、2、5行),而你要求:每个 sum=4 行必须留在它原本所在的“组块区间”内随机交换(类似局部打乱),则需更精细控制。此时应使用 transform 配合索引重映射:
# 为每组生成该组内随机重排后的索引序列,并按原顺序应用
shuffled_by_block = df.loc[
df.groupby('sum', sort=False)['sum']
.transform(lambda g: g.sample(frac=1, random_state=42).index)
].reset_index(drop=True)
⚠️ 注意:此方法依赖 transform 返回与原 Series 等长的索引映射,能精准维持每行在原始位置上的“组归属”,即使组内行物理分散也有效。
? 总结与最佳实践
- 默认首选:df.groupby(col, sort=False).sample(frac=1) —— 简洁、高效、语义清晰,适用于绝大多数分组连续场景;
- 强制组序稳定:若输入未排序但需输出按 col 升序排列组块,可用 df.sample(frac=1).sort_values(col, kind='stable')(stable 保证同值行相对顺序不变);
- 避免陷阱:勿省略 sort=False,否则 groupby 默认排序会打乱原始组序;务必设置 random_state 以确保结果可复现;
- 性能提示:对超大数据集,sample() 内部使用 NumPy 随机引擎,效率远高于手动循环 + shuffle。
掌握这一技巧,你就能在保持数据逻辑结构的前提下,灵活注入随机性——既满足算法鲁棒性要求,又不失业务可解释性。










