train test stratify stratifiedkfold sklearn shufflesplit one leave into example data cross array and python pandas numpy scikit-learn matrix-factorization

python - stratify - Sklearn train_test_split; reteniendo valores únicos de la(s) columna(s) en el conjunto de entrenamiento



stratifiedkfold (2)

¿Hay alguna manera de usar sklearn.model_selection.train_test_split para retener todos los valores únicos de una (s) columna (s) específica (s) en el conjunto de entrenamiento?

Déjame poner un ejemplo. El problema más común de factorización matricial que conozco es predecir las calificaciones de películas para los usuarios, por ejemplo, en los conjuntos de datos de Netflix Challenge o Movielens . Ahora bien, esta pregunta no se centra realmente en ningún enfoque de factorización de matriz única, pero dentro del rango de posibilidades hay un grupo que hará predicciones solo para combinaciones conocidas de usuarios y elementos.

Así, en Movielens 100k, por ejemplo, tenemos 943 usuarios únicos y 1682 películas únicas. Si tuviéramos que usar sklearn.model_selection.train_test_split incluso con una alta proporción de train_size (por ejemplo, 0.9), el número de usuarios únicos y películas no sería el mismo. Esto presenta un problema ya que el grupo de métodos que mencioné no podría predecir nada excepto 0 para películas o usuarios en los que no se había entrenado. Aquí hay un ejemplo de lo que quiero decir.

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

Intenta esto tantas veces como puedas y simplemente no terminarás con 1682 películas únicas en el tren. Esto es el resultado de una serie de películas que solo tienen una clasificación única en el conjunto de datos. Afortunadamente, lo mismo no es cierto para los usuarios (el número más bajo de calificaciones por usuario es 20), por lo que no es un problema. Pero para tener un set de entrenamiento en funcionamiento, necesitamos que todas las películas únicas estén en el set de entrenamiento al menos una vez. Además, no puedo utilizar la stratify= kwarg para sklearn.model_selection.train_test_split ya que no hay más de 1 entrada para todos los usuarios o para todas las películas.

Mi pregunta es la siguiente.

¿Hay alguna forma en Sklearn de dividir un conjunto de datos para garantizar que el conjunto de valores únicos de una columna específica se mantenga en el conjunto de entrenamiento?

Mi solución rudimentaria al problema es la siguiente.

  1. Separe los elementos que los usuarios tienen un número bajo de calificaciones totales.
  2. cree un sklearn.model_selection.train_test_split en los datos excluyendo estos ítems / usuarios raramente calificados (asegurando que el tamaño dividido + el tamaño excluido sea igual al tamaño dividido deseado).
  3. Combina los dos para obtener un conjunto de entrenamiento representativo final.

Ejemplo:

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))

Este enfoque funciona, pero solo tengo que sentir que hay una manera de lograr lo mismo con sklearn.model_selection.train_test_split u otro método de división desde sklearn.

Advertencia: los datos contienen entradas individuales para usuarios y películas

Mientras que el enfoque que propone @ serv-inc funcionaría para datos en los que cada clase está representada más de una vez. Ese no es el caso con estos datos, ni con la mayoría de los conjuntos de datos de recomendación / clasificación.


Lo que buscas se llama estratificación . Por suerte, sklearn tiene justo eso. Solo cambia la linea a

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

Si no se establece la stratify , los datos se barajan aleatoriamente. Consulte sklearn.model_selection.train_test_split

Si [ stratify no es None ], los datos se dividen de forma estratificada, utilizando esto como etiquetas de clase.

Actualización a la pregunta actualizada: parece que poner instancias únicas en el conjunto de entrenamiento no está integrado en scikit-learn . Podría abusar de PredefinedSplit o extend StratifiedShuffleSplit , pero esto podría ser más complicado que simplemente rodar el suyo.


Tal vez pueda agrupar sus datos de entrada en la película y luego tomar una muestra y luego combinar todas las muestras en un gran conjunto de datos.

# 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) . . .