Sklearn train_test_split; сохранение уникальных значений из столбца (ов) в обучающем наборе

Есть ли способ использовать sklearn.model_selection.train_test_split сохранить все уникальные значения из определенных столбцов в обучающем наборе.

Позвольте мне привести пример. Самая распространенная проблема факторизации матрицы, о которой я знаю, - это прогнозирование рейтингов фильмов для пользователей в наборах данных Netflix Challenge или Movielens. Теперь этот вопрос на самом деле не сосредоточен вокруг какого-либо отдельного матричного подхода факторизации, но в пределах диапазона возможностей существует группа, которая будет делать прогнозы только для известных комбинаций пользователей и элементов.

Так, например, в 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= карг для train_test_split поскольку существует не более 1 записи для всех пользователей или для всех фильмов.

У меня вопрос такой.

Есть ли способ в sklearn разделить набор данных, чтобы обеспечить сохранение набора уникальных значений из определенных столбцов в обучающем наборе?

Мое элементарное решение проблемы заключается в следующем.

  1. Разделите элементы, которые / пользователи имеют небольшое количество общих рейтингов.
  2. создать train_test_split на данных, исключая этих редко оцениваемых элементов / пользователей (гарантируя, что размер разделения + размер исключения будет равен желаемому размеру разделения).
  3. объединить два, чтобы получить окончательный представительский набор тренировок

Пример:

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 ответа

То, что вы ищете, называется стратификацией. К счастью, 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. Вы могли бы злоупотреблять PredefinedSplit или продлить StratifiedShuffleSplit, но это может быть сложнее, чем просто кататься самостоятельно.

Возможно, вы можете сгруппировать свои входные данные в фильме, затем взять образец и затем объединить все образцы в один большой набор данных.

# 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)
 .
 .
 .
Другие вопросы по тегам