如何在Pandas中按组等概率随机采样行(非固定每组行数)
本文介绍一种巧妙策略:先对每个分组均匀过采样,再全局随机抽取目标行数,从而实现各组被选中的概率近似相等,避免因组大小差异导致的偏差。
本文介绍一种巧妙策略:先对每个分组均匀过采样,再全局随机抽取目标行数,从而实现各组被选中的概率近似相等,避免因组大小差异导致的偏差。
在使用 pandas.DataFrame.groupby().sample() 时,若指定 n=k,则会强制从每个组中精确抽取 k 行——这适用于“每组取固定数量”的场景;但当目标是“让每个组被抽中的概率大致相同”(即组间无偏采样),而非保证每组样本数一致时,需采用更灵活的方法。
核心思路是两阶段采样:
- 第一阶段(组内均衡化):计算目标总样本量 N 与唯一组数 n_groups 的比值,向上取整得到每组应初步采样的行数 ceil(N / n_groups),并对每组独立、有放回地采样该数量;
- 第二阶段(全局随机化):从第一阶段结果中无放回地随机抽取 N 行,消除组内过采样引入的冗余,最终使各组出现频率趋近于 1 / n_groups。
以下是完整实现代码:
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_groups = df['ID'].nunique()
out = (df.groupby('ID')
.sample(n=math.ceil(N / n_groups), replace=True)
.sample(N) # 无放回全局重采样
.reset_index(drop=True))
print(out)
✅ 优势说明:
可赞AI
文字一秒可视化,免费AI办公神器
- 不依赖各组原始大小,即使某组仅1行(如 'b' 组),也能参与等概率竞争;
- 随着 N 增大,各组实际占比快速收敛至理论均值(如 3 组 → 每组 ≈ 33.3%);
- replace=True 在第一阶段确保小规模组不被忽略,sample(N) 在第二阶段保障输出严格为 N 行。
⚠️ 注意事项:
- 若 N < n_groups(如 N=2 但有3个组),math.ceil(2/3)=1,第一阶段每组采1行共3行,第二阶段再从中选2行——仍能保证组间公平;
- 该方法默认各组权重相同;如需自定义组权重(如按组大小加权),应改用 df.sample(frac=1, weights=...) 配合预计算权重列;
- 结果不保证每组至少出现一次(小概率事件),若需强制覆盖所有组,建议结合 groupby().apply(lambda g: g.sample(1)) 后补足剩余名额。
综上,该两阶段采样法以简洁代码实现了统计意义上的组间无偏抽样,是处理不均衡分组数据时的实用技巧。