Sklearn.LabelEncoder con valores nunca vistos antes

Si se ha instalado un sklearn.LabelEncoder en un conjunto de entrenamiento, podría romperse si encuentra nuevos valores cuando se usa en un conjunto de prueba.

La única solución que podría encontrar para esto es mapear todo lo nuevo en el conjunto de pruebas (es decir, no pertenecer a ninguna clase existente) a "" , y luego agregar explícitamente una clase correspondiente al LabelEncoder después:

 # train and test are pandas.DataFrame's and c is whatever column le = LabelEncoder() le.fit(train[c]) test[c] = test[c].map(lambda s: '' if s not in le.classes_ else s) le.classes_ = np.append(le.classes_, '') train[c] = le.transform(train[c]) test[c] = le.transform(test[c]) 

Esto funciona, pero ¿hay una mejor solución?

Actualizar

Como @sapo_cosmico señala en un comentario, parece que lo anterior ya no funciona, dado que asumo que es un cambio de implementación en LabelEncoder.transform , que ahora parece usar np.searchsorted (no sé si fue el caso anterior). Entonces, en lugar de LabelEncoder la clase a la lista de clases extraídas de LabelEncoder , se debe insertar en orden ordenado:

 import bisect le_classes = le.classes_.tolist() bisect.insort_left(le_classes, '') le.classes_ = le_classes 

Sin embargo, como todo esto se siente bastante torpe, estoy seguro de que hay un mejor enfoque para esto.

Terminé cambiando a Pandas ‘ get_dummies debido a este problema de datos invisibles.

  • Crea los maniquíes en los datos de entrenamiento.
    dummy_train = pd.get_dummies(train)
  • crear los dummies en el nuevo (datos ocultos)
    dummy_new = pd.get_dummies(new_data)
  • vuelva a indexar los nuevos datos a las columnas de los datos de entrenamiento, llenando los valores faltantes con 0
    dummy_new.reindex(columns = dummy_train.columns, fill_value=0)

Efectivamente, cualquier característica nueva que sea categórica no entrará en el clasificador, pero creo que no debería causar problemas, ya que no sabría qué hacer con ellos.

Me da la impresión de que lo que has hecho es muy similar a lo que hacen otras personas cuando se enfrentan a esta situación.

Ha habido algunos esfuerzos para agregar la capacidad de codificar tags invisibles a LabelEncoder (consulte especialmente https://github.com/scikit-learn/scikit-learn/pull/3483 y https://github.com/scikit-learn/ scikit-learn / pull / 3599 ), pero cambiar el comportamiento existente es en realidad más difícil de lo que parece a primera vista.

Por ahora, parece que el manejo de las tags “fuera de vocabulario” se deja a los usuarios individuales de scikit-learn.

LabelEncoder es básicamente un diccionario. Puedes extraerlo y usarlo para futuras codificaciones:

 from sklearn.preprocessing import LabelEncoder le = preprocessing.LabelEncoder() le.fit(X) le_dict = dict(zip(le.classes_, le.transform(le.classes_))) 

Recupere la etiqueta para un nuevo elemento, si falta un elemento, establezca el valor como desconocido

 le_dict.get(new_item, '') 

Recuperar tags para una columna Dataframe:

 df[your_col].apply(lambda x: le_dict.get(x, )) 

Conozco a dos desarrolladores que están trabajando en la construcción de envoltorios alrededor de transformadores y tuberías de Sklearn. Tienen 2 transformadores de codificador robustos (un codificador ficticio y uno de etiqueta) que pueden manejar valores invisibles. Aquí está la documentación de su biblioteca de skutil. Busque skutil.preprocessing.OneHotCategoricalEncoder o skutil.preprocessing.SafeLabelEncoder . En su SafeLabelEncoder() , los valores invisibles se codifican automáticamente a 999999.

Estaba tratando de resolver este problema y encontré dos formas prácticas de codificar datos categóricos de trenes de prueba y conjuntos con y sin utilizar LabelEncoder. Las nuevas categorías se rellenan con alguna cetegoría conocida “c” (como “otro” o “faltante”). El primer método parece funcionar más rápido. Espero que te ayude.

 import pandas as pd import time df=pd.DataFrame() df["a"]=['a','b', 'c', 'd'] df["b"]=['a','b', 'e', 'd'] #LabelEncoder + map t=time.clock() from sklearn.preprocessing import LabelEncoder le = LabelEncoder() suf="_le" col="a" df[col+suf] = le.fit_transform(df[col]) dic = dict(zip(le.classes_, le.transform(le.classes_))) col='b' df[col+suf]=df[col].map(dic).fillna(dic["c"]).astype(int) print(time.clock()-t) #--- #pandas category t=time.clock() df["d"] = df["a"].astype('category').cat.codes dic =df["a"].astype('category').cat.categories.tolist() df['f']=df['b'].astype('category',categories=dic).fillna("c").cat.codes df.dtypes print(time.clock()-t) 

Si solo se trata de entrenar y probar un modelo, ¿por qué no solo etiquetar en un conjunto de datos completo? Y luego use las clases generadas del objeto codificador.

 encoder = LabelEncoder() encoder.fit_transform(df["label"]) train_y = encoder.transform(train_y) test_y = encoder.transform(test_y) 

Recientemente me encontré con este problema y pude encontrar una solución bastante rápida al problema. Mi respuesta resuelve un poco más que este problema, pero también funcionará fácilmente para su problema. (Creo que es muy bueno)

Estoy trabajando con marcos de datos de pandas y originalmente usé sklearns labelencoder () para codificar mis datos, que luego utilizaría en otros módulos de mi progtwig.

Sin embargo, el codificador de tags en el preprocesamiento de sklearn no tiene la capacidad de agregar nuevos valores al algoritmo de encoding. Resolví el problema de codificar varios valores y guardar los valores de mapeo TAMBIÉN como capaces de agregar nuevos valores al codificador por (aquí hay un resumen de lo que hice):

 encoding_dict = dict() for col in cols_to_encode: #get unique values in the column to encode values = df[col].value_counts().index.tolist() # create a dictionary of values and corresponding number {value, number} dict_values = {value: count for value, count in zip(values, range(1,len(values)+1))} # save the values to encode in the dictionary encoding_dict[col] = dict_values # replace the values with the corresponding number from the dictionary df[col] = df[col].map(lambda x: dict_values.get(x)) 

Luego, simplemente puede guardar el diccionario en un archivo JSON y puede extraerlo y agregar el valor que desee agregando un nuevo valor y el valor entero correspondiente.

Explicaré un poco de razonamiento detrás de usar map () en lugar de reemplazar (). Descubrí que usar la función pandas replace () tomó más de un minuto para recorrer alrededor de 117,000 líneas de código. El uso del mapa llevó ese tiempo a poco más de 100 ms.

TLDR: en lugar de usar el preprocesamiento de sklearns, solo trabaja con tu dataframe creando un diccionario de mapeo y mapeando los valores por ti mismo.