用groupby().sample()可实现分层抽样:按列分组后每组独立随机抽,支持n(等量)或frac(比例)参数;需注意replace=True处理样本不足、reset_index()重置索引、多列分层自动跳过空组合。

用 sample() 配合 groupby() 实现分层抽样
分层随机抽样本质是「先按某列分组,再在每组内独立随机抽」。Pandas 没有内置的 stratified_sample 方法,但用 groupby().sample() 组合就能精准实现,且支持等比例或固定数量抽样。
常见错误是直接对整个 DataFrame 调用 sample(),结果不保证各层比例;或者用 apply(lambda x: x.sample(...)) 却忽略索引重置,导致后续合并出错。
- 若需每层抽相同数量(如每类 5 条):
df.groupby('category').sample(n=5, random_state=42) - 若需按比例抽(如每层抽 20%):
df.groupby('category').sample(frac=0.2, random_state=42) - 抽样后默认保留原始索引,如需连续整数索引,链式调用
.reset_index(drop=True)
处理某层样本不足时的 replace 参数
当某组实际行数少于指定的 n(例如某类只有 3 条却要求 n=5),默认会报 ValueError: Cannot take a larger sample than population when 'replace=False'。
这时必须显式设置 replace=True,否则代码中断——这不是 bug,而是 Pandas 的安全限制。
立即学习“Python免费学习笔记(深入)”;
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
- 允许重复抽样(有放回):
df.groupby('category').sample(n=5, replace=True, random_state=42) - 注意:
replace=True仅影响单组内部,不同组之间仍完全独立 - 若业务不允许重复,应提前过滤掉样本量不足的层:
df.groupby('category').filter(lambda x: len(x) >= 5).groupby('category').sample(n=5)
多分层变量(如按 'region' 和 'gender' 联合分层)
只需把分层变量写成列表传给 groupby(),Pandas 会自动按组合唯一值分组,无需手动构造新列。
容易忽略的是:联合分层后某些组合可能为空(比如 'North' + 'NonBinary' 在数据中不存在),此时 groupby 自动跳过,不会报错也不会补零。
- 两列联合分层抽样:
df.groupby(['region', 'gender']).sample(frac=0.1, random_state=42)
- 确保列名顺序不影响结果,但会影响分组键的 tuple 形式(如
('North', 'M')vs('M', 'North')) - 若需查看每组实际抽了多少条,可在抽样后接
.groupby(['region', 'gender']).size()
性能与内存注意事项
对千万级数据做 groupby().sample() 时,Pandas 会为每个组构建子 DataFrame,内存占用可能陡增,尤其当组数极多(如上万唯一 ID)时。
比 groupby().sample() 更轻量的替代方案是:先用 df.assign(stratum=df['col'].astype('category').cat.codes) 手动编码分层变量,再用 NumPy 向量化抽样逻辑——但这需要自己实现分层逻辑,通常只在极端性能场景下值得投入。
- 小到中等数据(groupby().sample() 最简洁可靠
- 抽样前建议用
df['stratum_col'].nunique()快速检查组数,避免意外生成海量小分组 - 若后续还需原数据其他列参与计算,别忘了抽样结果默认只含原始列,不自动带入额外信息

















