Pregunta sklearn.LabelEncoder con valores nunca antes vistos


Si un sklearn.LabelEncoder ha sido instalado 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 prueba (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). Entonces, en lugar de anexar <unknown> clase a la LabelEncoderlista de clases ya extraídas, 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.


32
2018-01-11 01:54


origen


Respuestas:


Terminé cambiando a Pandas ' get_dummies 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.


21
2017-11-17 15:53



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 (ver 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 etiquetas "fuera del vocabulario" se deja a los usuarios individuales de scikit-learn.


5
2018-06-17 06:47



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. Buscar skutil.preprocessing.OneHotCategoricalEncoder o skutil.preprocessing.SafeLabelEncoder. En su SafeLabelEncoder(), los valores no vistos se autocodifican a 999999.


1
2017-10-22 17:19



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.

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)

1
2018-01-09 13:27



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.

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

0
2017-07-13 09:26



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.


0
2017-08-07 20:48