二类不平衡数据集80-20拆分需求:确保稀有类同时存在于训练测试集
解决极端不平衡数据集的拆分问题(稀有类仅1个样本)
嘿,这个问题我之前也碰到过!当稀有类只有1个样本时,sklearn自带的train_test_split或者StratifiedShuffleSplit确实没法满足“同时出现在训练和测试集”的需求——毕竟它们只能把每个样本分配到其中一个集合里。不过咱们可以手动处理,轻松搞定这个问题,代码也很简单,完全适合Python新手~
核心思路
因为稀有类(label=0)只有1个样本,咱们需要让这个样本同时出现在训练集和测试集,然后从多数类(label=1)里按80-20的比例拆分出对应数量的样本,最后把两部分合并即可。这样既保证了稀有类在两个集合都存在,也满足了整体的拆分比例。
具体步骤(附代码)
假设你用pandas读取数据集(新手最常用的工具),咱们一步步来:
- 导入必要的库
import pandas as pd from sklearn.model_selection import train_test_split
- 读取并拆分数据集
先把数据分成稀有类和多数类两部分:
# 读取数据集(替换成你的数据文件路径) df = pd.read_csv('your_dataset.csv') # 分离稀有类(label=0)和多数类(label=1) df_rarity = df[df['label'] == 0] # 仅1个样本 df_majority = df[df['label'] == 1] # 500个样本
- 拆分多数类为训练和测试集
按80-20比例拆分多数类样本:
# test_size=0.2表示测试集占20%,random_state保证拆分结果可复现 train_majority, test_majority = train_test_split( df_majority, test_size=0.2, random_state=42 )
- 合并稀有类到训练和测试集
把唯一的稀有类样本分别加到训练集和测试集里:
# 训练集 = 多数类训练样本 + 稀有类样本 train_set = pd.concat([train_majority, df_rarity], ignore_index=True) # 测试集 = 多数类测试样本 + 稀有类样本 test_set = pd.concat([test_majority, df_rarity], ignore_index=True)
- 可选:打乱数据集顺序
如果需要打乱训练集和测试集的顺序,可添加这一步:
train_set = train_set.sample(frac=1, random_state=42).reset_index(drop=True) test_set = test_set.sample(frac=1, random_state=42).reset_index(drop=True)
验证结果
你可以检查两个集合的样本数和类别分布:
print(f"训练集总样本数:{len(train_set)},label=0的样本数:{len(train_set[train_set['label']==0])}") print(f"测试集总样本数:{len(test_set)},label=0的样本数:{len(test_set[test_set['label']==0])}")
输出应该是:
训练集总样本数:401,label=0的样本数:1 测试集总样本数:101,label=0的样本数:1
总样本数501,训练集占比≈80.04%,测试集≈20.16%,完美符合80-20的要求,而且稀有类样本在两个集合都存在啦!
为什么之前的方法不行?
StratifiedShuffleSplit这类分层方法会按类别比例拆分样本,但当某个类别只有1个样本时,它只能把这个样本分配到训练集或测试集其中一个——毕竟一个样本不能同时出现在两个地方(除非咱们手动复制它~)。
内容的提问来源于stack exchange,提问作者reddy




