【问题标题】:How to split Test and Train data such that there is garenteed at least one of each Class in each如何拆分测试和训练数据,以保证每个类中至少有一个
【发布时间】:2015-09-23 03:23:31
【问题描述】:

我正在尝试分类一些相当不平衡的数据。 但是,它的分类相当好。

为了准确评估效果,我必须将数据分成训练和测试子集。

现在我正在通过以下非常简单的措施来做到这一点:

import numpy as np
corpus = pandas.DataFrame(..., columns=["data","label"]) # My data, simplified
train_index = np.random.rand(len(corpus))>0.2
training_data = corpus[train_index]
test_data = corpus[np.logical_not(train_index)]

这很好也很简单,但是有些类很少出现: 在超过 50,000 个案例的语料库中,大约有 15 个每个出现不到 100 次,其中两个每个只出现一次。

我想将我的数据语料库划分为测试和训练子集,这样:

  • 如果一个类出现的次数少于两次,则将其排除在两次之外
  • 每个课程至少出现一次,在测试和训练中
  • 测试和训练的划分是随机的

我可以拼凑一些东西来做到这一点, (可能最简单的方法是删除出现次数少于 2 的东西),然后重新采样直到唾液在每一侧都有),但我想知道是否已经存在一种干净的方法。

我不认为sklearn.cross_validation.train_test_split 可以做到这一点,但它的存在表明 sklearn 可能具有这种功能。

【问题讨论】:

    标签: python pandas machine-learning scikit-learn classification


    【解决方案1】:

    以下满足您将数据划分为测试和训练的 3 个条件:

    #get rid of items with fewer than 2 occurrences.
    corpus=corpus[corpus.groupby('label').label.transform(len)>1]
    
    from sklearn.cross_validation import StratifiedShuffleSplit
    sss=StratifiedShuffleSplit(corpus['label'].tolist(), 1, test_size=0.5, random_state=None)
    
    train_index, test_index =list(*sss)
    training_data=corpus.iloc[train_index]
    test_data=corpus.iloc[test_index]
    

    我已经使用以下虚构数据框测试了上面的代码:

    #create random data with labels 0 to 39, then add 2 label case and one label case.     
    corpus=pd.DataFrame({'data':np.random.randn(49998),'label':np.random.randint(40,size=49998)})
    corpus.loc[49998]=[random.random(),40]
    corpus.loc[49999]=[random.random(),40]
    corpus.loc[50000]=[random.random(),41]
    

    在测试代码时会产生以下输出:

    test_data[test_data['label']==40]
    Out[110]: 
               data  label
    49999  0.231547     40
    
    training_data[training_data['label']==40]
    Out[111]: 
               data  label
    49998  0.253789     40
    
    test_data[test_data['label']==41]
    Out[112]: 
    Empty DataFrame
    Columns: [data, label]
    Index: []
    
    training_data[training_data['label']==41]
    Out[113]: 
    Empty DataFrame
    Columns: [data, label]
    Index: []
    

    【讨论】:

    • 不应该使用forloop:它应该是train_indexes, test_indexes = list(*splitter); train_data = common_corpus.iloc[train_indexes,:]; test_data = common_corpus.iloc[test_indexes,:]
    • 你是对的@Oxinabox,这是一种更清洁的方式。如果您对数据进行多次拆分,该循环对于StratifiedShuffleSplit() 中大于1 的n_iter 值最有用。我将使用您建议的替换来修改答案。
    • 不应该是iloc而不是loc,所以如果索引不是一个简单的范围,它就可以正常工作吗?
    • iloc 是更好的答案,我修改了答案以反映这一点。谢谢。
    猜你喜欢
    • 2019-10-11
    • 2018-01-01
    • 2021-01-28
    • 2019-08-28
    • 2019-05-01
    • 1970-01-01
    • 2016-10-24
    • 2018-06-04
    • 1970-01-01
    相关资源
    最近更新 更多