values sklearn preprocessing onehotencoder one hot guide example estimator categorical_features categorical python scikit-learn

python - preprocessing - sklearn.LabelEncoder con valores nunca antes vistos



onehotencoder onehotencoder categorical_features 1]) (6)

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

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 perteneciente a ninguna clase existente) a "<unknown>" , 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: ''<unknown>'' if s not in le.classes_ else s) le.classes_ = np.append(le.classes_, ''<unknown>'') train[c] = le.transform(train[c]) test[c] = le.transform(test[c])

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

Actualizar

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

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

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


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

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


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

Ha habido un cierto esfuerzo para agregar la capacidad de codificar etiquetas 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 realmente más difícil de lo que parece a primera vista.

Por ahora, parece que el manejo de etiquetas "fuera del vocabulario" se deja a los usuarios individuales de scikit-learn.


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

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

Sin embargo, el codificador de etiqueta en el preprocesamiento de sklearn no tiene la capacidad de agregar nuevos valores al algoritmo de codificación. Resolví el problema de codificar valores múltiples y guardar los valores de asignación ASÍ como poder agregar nuevos valores al codificador (aquí hay un bosquejo 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 puede simplemente guardar el diccionario en un archivo JSON y puede extraerlo y agregar cualquier valor que desee agregando un nuevo valor y el valor entero correspondiente.

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

TLDR: en lugar de utilizar sklearns, el preprocesamiento solo funciona con su marco de datos creando un diccionario de mapeo y elaborando los valores usted mismo.


Si solo se trata de entrenar y probar un modelo, ¿por qué no solo labelencode en todo el conjunto de datos? Y luego usa las clases generadas desde el objeto del codificador.

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)


Terminé cambiando a los get_dummies Pandas debido a este problema de datos no vistos.

  • crear los maniquíes en los datos de entrenamiento
    dummy_train = pd.get_dummies(train)
  • crear los maniquíes en el nuevo (datos no vistos)
    dummy_new = pd.get_dummies(new_data)
  • vuelva a indexar los datos nuevos en las columnas de los datos de entrenamiento, completando los valores que faltan 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.