Sklearn train_test_split;保留训练集中列的唯一值

时间:2017-12-07 17:02:03

标签: python pandas numpy scikit-learn matrix-factorization

有没有办法使用sklearn.model_selection.train_test_split来保留训练集中特定列的所有唯一值。

让我举一个例子。我所知道的最常见的矩阵分解问题是预测用户的电影评级在Netflix ChallengeMovielens数据集中说明。现在这个问题并没有真正围绕任何单一的矩阵分解方法,但在可能性的范围内,有一个组只能对已知的用户和项目组合进行预测。

因此,在Movielens 100k中,我们拥有943个独特用户和1682个独特电影。如果我们使用train_test_split即使比率train_size较高(比如说0.9),那么唯一身份用户和电影的数量就不一样了。这提出了一个问题,因为我提到的一组方法无法为电影或未经过培训的用户预测0。这是我的意思的一个例子。

import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split

ml = pd.read_csv('ml-100k/u.data', sep='\t', names=['User_id', 'Item_id', 'Rating', 'ts'])
ml.head()   
   User_id  Item_id Rating         ts
0      196      242      3  881250949
1      186      302      3  891717742
2       22      377      1  878887116
3      244       51      2  880606923
4      166      346      1  886397596
ml.User_id.unique().size
943
ml.Item_id.unique().size
1682
utrain, utest, itrain, itest, rtrain, rtest = train_test_split(ml, train_size=0.9)
np.unique(utrain).size
943
np.unique(itrain).size
1644

尽可能多地试试这个,你最终不会在列车中找到1682个独特的电影。这是许多电影在数据集中只有一个评级的结果。幸运的是,对于用户来说情况并非如此(用户的最低评级数是20),所以这不是问题。但是为了拥有一个有效的训练集,我们需要所有独特的电影至少在训练集中一次。此外,我无法将stratify= kwarg用于train_test_split,因为所有用户或所有电影的条目不会超过1个。

我的问题是这个。

sklearn是否有办法拆分数据集以确保特定列中的唯一值集保留在训练集中?

我对这个问题的初步解决方法如下。

  1. 分隔/用户总评分较低的项目。
  2. 在数据上创建train_test_split,不包括这些很少评级的商品/用户(确保拆分尺寸+排除尺寸等于您所需的拆分尺寸)。
  3. 将两者合并以获得最终的代表性训练集
  4. 示例:

    item_counts = ml.groupby(['Item_id']).size()
    user_counts = ml.groupby(['User_id']).size()
    rare_items = item_counts.loc[item_counts <= 5].index.values
    rare_users = user_counts.loc[user_counts <= 5].index.values
    rare_items.size
    384
    rare_users.size
    0
    # We can ignore users in this example
    rare_ratings = ml.loc[ml.Item_id.isin(rare_items)]
    rare_ratings.shape[0]
    968
    ml_less_rare = ml.loc[~ml.Item_id.isin(rare_items)]
    items = ml_less_rare.Item_id.values
    users = ml_less_rare.User_id.values
    ratings = ml_less_rare.Rating.values
    # Establish number of items desired from train_test_split
    desired_ratio = 0.9
    train_size = desired_ratio * ml.shape[0] - rare_ratings.shape[0]
    train_ratio = train_size / ml_less_rare.shape[0]
    itrain, itest, utrain, utest, rtrain, rtest = train_test_split(items, users, ratings, train_size=train_ratio)
    itrain = np.concatenate((itrain, rare_ratings.Item_id.values))
    np.unique(itrain).size
    1682
    utrain = np.concatenate((utrain, rare_ratings.User_id.values))
    np.unique(utrain).size
    943
    rtrain = np.concatenate((rtrain, rare_ratings.Rating.values))
    

    这种方法有效,但我只需要感觉有一种方法可以使用train_test_split或sklearn中的另一种分割方法来实现相同的效果。

    警告 - 数据包含用户和电影的单个条目

    虽然@ serv-inc提出的方法适用于每个类不止一次表示的数据。这种数据不是这种情况,也不是大多数推荐/排名数据集。

2 个答案:

答案 0 :(得分:2)

您正在寻找的是分层。幸运的是,sklearn就是这样。只需将行更改为

即可
itrain, itest, utrain, utest, rtrain, rtest = train_test_split(
     items, users, ratings, train_size=train_ratio, stratify=users)

如果未设置stratify,则会随机对数据进行随机播放。见http://scikit-learn.org/stable/modules/generated/sklearn.model_selection.train_test_split.html

  

如果[stratify不是None,则数据会以分层方式拆分,并将其作为类标签。

更新到更新的问题:似乎将唯一的实例放入训练集中未构建到scikit-learn 中。您可以滥用PredefinedSplitextend StratifiedShuffleSplit,但这可能比简单地滚动自己更复杂。

答案 1 :(得分:0)

也许你可以将你的输入数据分组到电影上,然后取样,然后将所有样本合并成一个大数据集。

# initialize lists
utrain_all =[]
utest_all =[]
itrain_all = []
itest_all = []
rtrain_all = []
rtest__all = []

grp_ml = ml.groupby('Item_id')
for name, group in grp_ml:
 utrain, utest, itrain, itest, rtrain, rtest = train_test_split(group, train_size=0.9)
 utrain_all.append(utrain)
 utest_all.append(utest)
 itrain_all.append(itrain)
 .
 .
 .