scikit-learn: una encoding en caliente de funciones categóricas de cadenas

Estoy tratando de realizar una encoding en caliente de un conjunto de datos trivial.

data = [['a', 'dog', 'red'] ['b', 'cat', 'green']] 

¿Cuál es la mejor manera de preprocesar estos datos utilizando Scikit-Learn?

En primer instinto, mirarías hacia OneHotEncoder de Scikit -Learn. Pero el único codificador activo no admite cadenas como características; sólo discretiza enteros.

Entonces, usarías un LabelEncoder , que codificaría las cadenas en números enteros. Pero luego tiene que aplicar el codificador de tags en cada una de las columnas y almacenar cada uno de estos codificadores de tags (así como las columnas en las que se aplicaron). Y esto se siente extremadamente torpe.

Entonces, ¿cuál es la mejor manera de hacerlo en Scikit-Learn?

Por favor, no sugiera pandas.get_dummies . Eso es lo que generalmente uso hoy en día para las codificaciones en caliente. Sin embargo, está limitado en el hecho de que no puede codificar su conjunto de entrenamiento / prueba por separado.

Si estás en sklearn> 0.20.dev0

 In [11]: from sklearn.preprocessing import OneHotEncoder ...: cat = OneHotEncoder() ...: X = np.array([['a', 'b', 'a', 'c'], [0, 1, 0, 1]], dtype=object).T ...: cat.fit_transform(X).toarray() ...: Out[11]: array([[1., 0., 0., 1., 0.], [0., 1., 0., 0., 1.], [1., 0., 0., 1., 0.], [0., 0., 1., 0., 1.]]) 

Si estás en sklearn == 0.20.dev0

 In [30]: cat = CategoricalEncoder() In [31]: X = np.array([['a', 'b', 'a', 'c'], [0, 1, 0, 1]], dtype=object).T In [32]: cat.fit_transform(X).toarray() Out[32]: array([[ 1., 0., 0., 1., 0.], [ 0., 1., 0., 0., 1.], [ 1., 0., 0., 1., 0.], [ 0., 0., 1., 0., 1.]]) 

Otra forma de hacerlo es usar category_encoders.

Aquí hay un ejemplo:

 % pip install category_encoders import category_encoders as ce le = ce.OneHotEncoder(return_df=False, impute_missing=False, handle_unknown="ignore") X = np.array([['a', 'dog', 'red'], ['b', 'cat', 'green']]) le.fit_transform(X) array([[1, 0, 1, 0, 1, 0], [0, 1, 0, 1, 0, 1]]) 

Muy buena pregunta.

Sin embargo, en cierto sentido, es un caso privado de algo que surge (al menos para mí) con bastante frecuencia: dadas sklearn etapas de sklearn aplicables a los subconjuntos de la matriz X , me gustaría aplicar (posiblemente varias) dada la matriz completa . Aquí, por ejemplo, tiene una etapa que se sabe que se ejecuta en una sola columna, y le gustaría aplicarlo tres veces, una por columna.

Este es un caso clásico para usar el patrón de diseño compuesto .

Aquí hay un (bosquejo de a) etapa reutilizable que acepta un diccionario que mapea un índice de columna en la transformación para aplicarlo:

 class ColumnApplier(object): def __init__(self, column_stages): self._column_stages = column_stages def fit(self, X, y): for i, k in self._column_stages.items(): k.fit(X[:, i]) return self def transform(self, X): X = X.copy() for i, k in self._column_stages.items(): X[:, i] = k.transform(X[:, i]) return X 

Ahora, para usarlo en este contexto, comenzando con

 X = np.array([['a', 'dog', 'red'], ['b', 'cat', 'green']]) y = np.array([1, 2]) X 

solo lo usaría para asignar el índice de cada columna a la transformación que desea:

 multi_encoder = \ ColumnApplier(dict([(i, preprocessing.LabelEncoder()) for i in range(3)])) multi_encoder.fit(X, None).transform(X) 

Una vez que desarrollas una etapa de este tipo (no puedo publicar la que uso), puedes usarla una y otra vez para diferentes configuraciones.

He enfrentado este problema muchas veces y encontré una solución en este libro en su página 100:

Podemos aplicar ambas transformaciones (de categorías de texto a categorías de enteros, luego de categorías de enteros a vectores de un solo uso) en una sola toma usando la clase LabelBinarizer:

y el código de muestra está aquí:

 from sklearn.preprocessing import LabelBinarizer encoder = LabelBinarizer() housing_cat_1hot = encoder.fit_transform(data) housing_cat_1hot 

y como resultado: tenga en cuenta que esto devuelve una matriz NumPy densa de forma predeterminada. Puede obtener una matriz dispersa al pasar sparse_output = True al constructor LabelBinarizer.

Y puede encontrar más información sobre el LabelBinarizer, aquí en la documentación oficial de sklearn