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 разделить набор данных, чтобы обеспечить сохранение набора уникальных значений из определенных столбцов в обучающем наборе?
Мое элементарное решение проблемы заключается в следующем.
- Разделите элементы, которые / пользователи имеют небольшое количество общих рейтингов.
- создать
train_test_split
на данных, исключая этих редко оцениваемых элементов / пользователей (гарантируя, что размер разделения + размер исключения будет равен желаемому размеру разделения). - объединить два, чтобы получить окончательный представительский набор тренировок
Пример:
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)
.
.
.