DataFrame随机抽样:需保证每组最小样本量且保留原分布
实现带分组最小样本要求的加权随机抽样(保留原数据分布)
我完全懂你的需求——既要做随机抽样,又得保证目标分组列的每个类别至少有1个样本,同时还要尽量贴合原数据里各分组的样本量占比(也就是保留原分布特征)。针对你给出的示例数据,咱们可以通过先给每个分组保底抽1个样本,再对剩余名额按原分布加权抽样的方式来完美实现。
先看你的示例数据
先把你的测试数据构造出来:
import pandas as pd # 构造示例DataFrame df = pd.DataFrame({'class': [0]*13 + [1, 2]})
这里class列里,0占了13个样本,1和2各1个,要抽4个样本的话,刚好每个组先拿1个,剩下1个名额给占比最高的0,就是你期望的结果。
分步实现抽样逻辑
1. 先抽取每个分组的保底样本
首先给每个class类别至少抽1个样本,确保每个分组都有代表:
# 每个分组抽取1个样本,random_state用于结果可复现,可按需调整 min_samples = df.groupby('class').sample(n=1, random_state=42)
2. 计算剩余需要抽取的样本数
total_target = 4 # 你想要的总抽样数量 remaining_quota = total_target - len(min_samples)
这里len(min_samples)是3(3个分组),所以还剩1个名额需要抽取。
3. 按原分布加权抽取剩余名额
接下来要从剩下的样本里,按照原数据的分组占比来抽,保证抽样结果的分布和原数据一致:
# 计算原数据各分组的样本占比,作为抽样权重 class_weights = df['class'].value_counts(normalize=True) # 排除已经抽过的保底样本,得到剩余可抽样的数据集 remaining_data = df.drop(min_samples.index) # 按权重抽取剩余名额,这里权重用原分布的占比,确保贴合原数据特征 additional_samples = remaining_data.sample( n=remaining_quota, weights=remaining_data['class'].map(class_weights), random_state=42 )
4. 合并结果并整理
把保底样本和额外抽取的样本合并,再排序整理一下:
df_sample = pd.concat([min_samples, additional_samples]).sort_values('class').reset_index(drop=True)
查看最终结果
打印df_sample就能得到你期望的输出:
print(df_sample)
输出:
class 0 0 1 0 2 1 3 2
通用适配说明
这个方法适用于任何满足总抽样数≥分组数量的场景:
- 如果某个分组本身样本量只有1个(比如示例里的1和2),额外抽样时不会再选中它,因为没有剩余样本了,符合逻辑;
- 如果总抽样数更大,比如要抽5个,那剩余2个名额都会给占比最高的0,完美保留原数据的分布特征。
内容的提问来源于stack exchange,提问作者s900n
相关产品推荐
相关产品推荐

