使用pandas groupby按比例抽样时,如何保留所有极小分组?
问题根因
你遇到的小分组完全未进入抽样结果的问题,本质是pandas sample 方法的内置处理逻辑导致的:
- 当传入
frac参数时,pandas会先计算每个分组的实际抽样条数 = 分组总样本量 × 抽样比例 - 若计算出的抽样条数不足1,会直接返回空样本,不会做向上取整或者兜底处理
在你的示例数据里,b=1的分组总共只有1条样本,按0.1比例算得需要抽0.1条,不足1条就被直接舍弃,自然不会出现在最终结果里。
调整实现方案
方案1:自定义逐组抽样逻辑,设置抽样数最小阈值
不再直接传frac参数让pandas自动计算抽样数,而是在分组内手动计算每个组的目标抽样数,给抽样数设置最小为1的兜底值——只要分组非空,哪怕按比例算出的抽样数不足1,也至少抽1条,从根源上避免小分组被完全漏掉。
# 固定抽样比例 sample_frac = 0.1 # 固定随机种子保证结果可复现 df1 = df.groupby('b', group_keys=False).apply( lambda g: g.sample( n = min( len(g), # 抽样数不能超过组本身的总样本量 max(1, round(len(g) * sample_frac)) # 抽样数最少为1 ), random_state=42 ) ).reset_index(drop=True)
逻辑说明:
- 对b=0的大分组(12条样本),按0.1比例算出需要抽1条,和原逻辑的抽样规模一致
- 对b=1的小分组(1条样本),按0.1比例算出需要抽0.1条,触发兜底逻辑至少抽1条,因为组总共只有1条,最终会把整个组保留,完全符合需求。
- 附带的
group_keys=False参数会关闭分组键索引的生成,避免结果出现多层索引,让后续的reset_index操作更顺畅。
方案2:分块处理大小分组(大表性能更优)
如果是数据量百万级以上的大表,逐组在apply内计算抽样数会有额外性能开销,可以直接把小分组和大分组拆成两部分处理,最后合并结果:
sample_frac = 0.1 # 统计每个分组的总样本量 group_size_stat = df.groupby('b').size() # 筛选出“按比例抽样肯定抽不到”的小分组:len(g)*frac < 1 small_group_keys = group_size_stat[group_size_stat * sample_frac < 1].index # 小分组直接全量保留 small_sample = df[df['b'].isin(small_group_keys)] # 剩余大分组按原逻辑正常按比例抽样 big_sample = df[~df['b'].isin(small_group_keys)].groupby( 'b', group_keys=False ).apply(lambda g: g.sample(frac=sample_frac, random_state=42)) # 合并两部分抽样结果 df1 = pd.concat([small_sample, big_sample]).reset_index(drop=True)
注意:如果你的需求是“只要分组样本量极小,不管算出来的抽样数是多少,都要全量保留”,可以调整小分组的判定阈值(比如把判定条件改成
len(g) < 5就全量保留),不用完全绑定抽样比例的计算结果。
内容的提问来源于stack exchange,提问作者Feng Chen
相关产品推荐
相关产品推荐

