
本文介绍一种灵活的 Pandas 分组采样策略:不强制每组抽取相同行数,而是使每个分组被选中的整体概率均等,适用于组大小差异大、且需保持组间采样公平性的场景。
本文介绍一种灵活的 pandas 分组采样策略:不强制每组抽取相同行数,而是使每个分组被选中的**整体概率均等**,适用于组大小差异大、且需保持组间采样公平性的场景。
在实际数据分析中,我们常需从分组数据中随机采样。但 df.groupby("ID").sample(n=k) 会严格要求每组固定抽取 k 行——当组内样本量悬殊(如某组仅1条、另一组有上千条)时,这种“等频采样”反而导致小组合被过度代表,违背“组级等概率”的初衷。
真正意义上的组级等概率采样,是指:最终采样的 N 行中,每一行来自任意一个组的概率近似相等(即各组被抽中的总次数占比 ≈ 1 / 组数),而非每组输出行数相同。Pandas 原生 sample() 不直接支持该语义,但可通过两阶段采样优雅实现:
- 第一阶段(组内过采样):对每个组,按 ⌈N / 总组数⌉ 进行有放回采样,确保各组至少提供足够候选;
- 第二阶段(全局重采样):从所有候选行中无放回随机抽取 N 行,打破组内数量偏差,使组间入选概率趋于均衡。
import pandas as pd
import math
# 示例数据
df = pd.DataFrame({
'ID': ['a', 'a', 'b', 'c', 'c', 'c'],
'Value': [2, 4, 6, 8, 10, 12]
})
N = 4
n_per_group = math.ceil(N / df['ID'].nunique()) # 本例中为 ceil(4/3) = 2
result = (df.groupby('ID')
.sample(n=n_per_group, replace=True) # 每组至少提供2个候选
.sample(N, replace=False)) # 全局随机选4行
print(result)
✅ 优势说明:
- 当 N 较大(如 N=100),各组采样比例将稳定趋近 1/3 ≈ 33.3%,验证了组级概率均衡性;
- 支持 replace=True 或 False 的灵活组合,适配不同数据规模;
- 时间复杂度可控,无需手动构造权重或使用 numpy.random.choice 手动分配概率。
⚠️ 注意事项:
- 若某组原始行数极少(如仅1行)且 N 极大,第一阶段 replace=True 是必要的,否则无法生成足够候选;
- 第二阶段建议使用 replace=False(默认),避免单组因过采样而主导结果;
- 如需严格控制方差,可重复执行多次并取比例最均衡的结果,或改用分层抽样库(如 scikit-learn.model_selection.StratifiedShuffleSplit),但后者要求标签列必须为分类目标变量。
该方法以简洁代码实现了统计意义上的组公平采样,在 A/B 测试分组、模型训练集平衡、报表抽样等场景中兼具实用性与鲁棒性。










