¿Cómo puedo codificar en caliente en Python?

Tengo un problema de clasificación de aprendizaje automático con 80% de variables categóricas. ¿Debo usar una encoding en caliente si quiero usar algún clasificador para la clasificación? ¿Puedo pasar los datos a un clasificador sin la encoding?

Estoy tratando de hacer lo siguiente para la selección de características:

  1. Leí el archivo del tren:

    num_rows_to_read = 10000 train_small = pd.read_csv("../../dataset/train.csv", nrows=num_rows_to_read) 
  2. Cambio el tipo de las características categóricas a ‘categoría’:

     non_categorial_features = ['orig_destination_distance', 'srch_adults_cnt', 'srch_children_cnt', 'srch_rm_cnt', 'cnt'] for categorical_feature in list(train_small.columns): if categorical_feature not in non_categorial_features: train_small[categorical_feature] = train_small[categorical_feature].astype('category') 
  3. Yo uso una encoding en caliente:

     train_small_with_dummies = pd.get_dummies(train_small, sparse=True) 

El problema es que la 3ª parte a menudo se atasca, aunque estoy usando una máquina fuerte.

Por lo tanto, sin la única encoding en caliente no puedo hacer ninguna selección de características para determinar la importancia de las características.

¿Que recomiendas?

Enfoque 1: puede usar get_dummies en el dataframe de pandas.

Ejemplo 1:

 import pandas as pd s = pd.Series(list('abca')) pd.get_dummies(s) Out[]: abc 0 1.0 0.0 0.0 1 0.0 1.0 0.0 2 0.0 0.0 1.0 3 1.0 0.0 0.0 

Ejemplo 2:

Lo siguiente transformará una columna dada en una hot. Utilice el prefijo para tener múltiples maniquíes.

 import pandas as pd df = pd.DataFrame({ 'A':['a','b','a'], 'B':['b','a','c'] }) df Out[]: AB 0 ab 1 ba 2 ac # Get one hot encoding of columns B one_hot = pd.get_dummies(df['B']) # Drop column B as it is now encoded df = df.drop('B',axis = 1) # Join the encoded df df = df.join(one_hot) df Out[]: A abc 0 a 0 1 0 1 b 1 0 0 2 a 0 0 1 

Enfoque 2: utilizar Scikit-learn

Dado un conjunto de datos con tres características y cuatro muestras, permitimos que el codificador encuentre el valor máximo por característica y transforme los datos en una encoding binaria.

 >>> from sklearn.preprocessing import OneHotEncoder >>> enc = OneHotEncoder() >>> enc.fit([[0, 0, 3], [1, 1, 0], [0, 2, 1], [1, 0, 2]]) OneHotEncoder(categorical_features='all', dtype=, handle_unknown='error', n_values='auto', sparse=True) >>> enc.n_values_ array([2, 3, 4]) >>> enc.feature_indices_ array([0, 2, 5, 9], dtype=int32) >>> enc.transform([[0, 1, 1]]).toarray() array([[ 1., 0., 0., 1., 0., 0., 1., 0., 0.]]) 

Aquí está el enlace para este ejemplo: http://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.OneHotEncoder.html

Puede hacerlo con numpy.eye y usando el mecanismo de selección de elementos de matriz:

 import numpy as np nb_classes = 6 data = [[2, 3, 4, 0]] def indices_to_one_hot(data, nb_classes): """Convert an iterable of indices to one-hot encoded labels.""" targets = np.array(data).reshape(-1) return np.eye(nb_classes)[targets] 

El valor de retorno de indices_to_one_hot(nb_classes, data) es ahora

 array([[[ 0., 0., 1., 0., 0., 0.], [ 0., 0., 0., 1., 0., 0.], [ 0., 0., 0., 0., 1., 0.], [ 1., 0., 0., 0., 0., 0.]]]) 

La .reshape(-1) está ahí para asegurarse de que tiene el formato de tags correcto (también puede tener [[2], [3], [4], [0]] ).

En primer lugar, la forma más fácil de codificar en caliente: usar Sklearn.

http://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.OneHotEncoder.html

En segundo lugar, no creo que usar pandas para una encoding en caliente sea tan simple (aunque no está confirmado)

Creando variables ficticias en pandas para python

Por último, ¿es necesario para usted una encoding en caliente? Una encoding en caliente aumenta exponencialmente el número de funciones, lo que aumenta drásticamente el tiempo de ejecución de cualquier clasificador o cualquier otra cosa que vaya a ejecutar. Especialmente cuando cada característica categórica tiene muchos niveles. En su lugar puedes hacer encoding ficticia.

El uso de la encoding ficticia generalmente funciona bien, por mucho menos tiempo de ejecución y complejidad. Un sabio profesor una vez me dijo: ‘Menos es más’.

Aquí está el código para mi función de encoding personalizada si lo desea.

 from sklearn.preprocessing import LabelEncoder #Auto encodes any dataframe column of type category or object. def dummyEncode(df): columnsToEncode = list(df.select_dtypes(include=['category','object'])) le = LabelEncoder() for feature in columnsToEncode: try: df[feature] = le.fit_transform(df[feature]) except: print('Error encoding '+feature) return df 

EDITAR: La comparación para ser más claro:

Codificación instantánea: convierte n niveles en n-1 columnas.

 Index Animal Index cat mouse 1 dog 1 0 0 2 cat --> 2 1 0 3 mouse 3 0 1 

Puede ver cómo esto explotará su memoria si tiene muchos tipos (o niveles) diferentes en su función categórica. Ten en cuenta que esto es solo UNA columna.

Codificación ficticia:

 Index Animal Index Animal 1 dog 1 0 2 cat --> 2 1 3 mouse 3 2 

Convertir a representaciones numéricas en su lugar. Ahorra mucho espacio de funciones, a costa de un poco de precisión.

Una encoding en caliente con pandas es muy fácil:

 def one_hot(df, cols): """ @param df pandas DataFrame @param cols a list of columns to encode @return a DataFrame with one-hot encoding """ for each in cols: dummies = pd.get_dummies(df[each], prefix=each, drop_first=False) df = pd.concat([df, dummies], axis=1) return df 

EDITAR:

Otra forma de one_hot usando el LabelBinarizer de LabelBinarizer :

 from sklearn.preprocessing import LabelBinarizer label_binarizer = LabelBinarizer() label_binarizer.fit(all_your_labels_list) # need to be global or remembered to use it later def one_hot_encode(x): """ One hot encode a list of sample labels. Return a one-hot encoded vector for each label. : x: List of sample Labels : return: Numpy array of one-hot encoded labels """ return label_binarizer.transform(x) 

Puedes usar la función numpy.eye.

 import numpy as np def one_hot_encode(x, n_classes): """ One hot encode a list of sample labels. Return a one-hot encoded vector for each label. : x: List of sample Labels : return: Numpy array of one-hot encoded labels """ return np.eye(n_classes)[x] def main(): list = [0,1,2,3,4,3,2,1,0] n_classes = 5 one_hot_list = one_hot_encode(list, n_classes) print(one_hot_list) if __name__ == "__main__": main() 

Resultado

 D:\Desktop>python test.py [[ 1. 0. 0. 0. 0.] [ 0. 1. 0. 0. 0.] [ 0. 0. 1. 0. 0.] [ 0. 0. 0. 1. 0.] [ 0. 0. 0. 0. 1.] [ 0. 0. 0. 1. 0.] [ 0. 0. 1. 0. 0.] [ 0. 1. 0. 0. 0.] [ 1. 0. 0. 0. 0.]] 

Mucho más fácil de usar Pandas para la encoding básica de un solo calor. Si estás buscando más opciones puedes usar scikit-learn.

Para la encoding básica de un solo uso con Pandas , simplemente pase su dataframe a la función get_dummies .

Por ejemplo, si tengo un dataframe llamado imdb_movies :

introduzca la descripción de la imagen aquí

… y quiero una encoding en caliente de la columna Clasificación, simplemente hago esto:

 pd.get_dummies(imdb_movies.Rated) 

introduzca la descripción de la imagen aquí

Esto devuelve un nuevo dataframe con una columna para cada ” nivel ” de calificación que existe, junto con un 1 o un 0 que especifican la presencia de esa calificación para una observación determinada.

Por lo general, queremos que esto sea parte del dataframe original. En este caso, simplemente adjuntamos nuestro nuevo marco codificado ficticio al marco original usando ” encuadernación de columna “.

Podemos enlazar en columna usando la función concat de Pandas:

 rated_dummies = pd.get_dummies(imdb_movies.Rated) pd.concat([imdb_movies, rated_dummies], axis=1) 

introduzca la descripción de la imagen aquí

Ahora podemos ejecutar el análisis en nuestro dataframe completo.

FUNCION DE UTILIDAD SIMPLE

Recomendaría hacerte una función de utilidad para hacer esto rápidamente:

 def encode_and_bind(original_dataframe, feature_to_encode): dummies = pd.get_dummies(original_dataframe[[feature_to_encode]]) res = pd.concat([original_dataframe, dummies], axis=1) return(res) 

Uso

 encode_and_bind(imdb_movies, 'Rated') 

Resultado :

introduzca la descripción de la imagen aquí

Además, según el comentario de @pmalbu, si desea que la función elimine el feature_to_encode original , use esta versión:

 def encode_and_bind(original_dataframe, feature_to_encode): dummies = pd.get_dummies(original_dataframe[[feature_to_encode]]) res = pd.concat([original_dataframe, dummies], axis=1) res = res.drop([feature_to_encode], axis=1) return(res) 

La encoding instantánea requiere un poco más que convertir los valores en variables indicadoras. Por lo general, el proceso ML requiere que aplique esta encoding varias veces para validar o probar conjuntos de datos y aplicar el modelo que construye a los datos observados en tiempo real. Debe almacenar la asignación (transformación) que se utilizó para construir el modelo. Una buena solución usaría el DictVectorizer o LabelEncoder (seguido de get_dummies . Aquí hay una función que puede usar:

 def oneHotEncode2(df, le_dict = {}): if not le_dict: columnsToEncode = list(df.select_dtypes(include=['category','object'])) train = True; else: columnsToEncode = le_dict.keys() train = False; for feature in columnsToEncode: if train: le_dict[feature] = LabelEncoder() try: if train: df[feature] = le_dict[feature].fit_transform(df[feature]) else: df[feature] = le_dict[feature].transform(df[feature]) df = pd.concat([df, pd.get_dummies(df[feature]).rename(columns=lambda x: feature + '_' + str(x))], axis=1) df = df.drop(feature, axis=1) except: print('Error encoding '+feature) #df[feature] = df[feature].convert_objects(convert_numeric='force') df[feature] = df[feature].apply(pd.to_numeric, errors='coerce') return (df, le_dict) 

Esto funciona en un dataframe de pandas y para cada columna del dataframe que crea y devuelve una asignación. Así que lo llamarías así:

 train_data, le_dict = oneHotEncode2(train_data) 

Luego, en los datos de prueba, la llamada se realiza pasando el diccionario devuelto de la capacitación:

 test_data, _ = oneHotEncode2(test_data, le_dict) 

Un método equivalente es utilizar DictVectorizer . Un post relacionado sobre el mismo está en mi blog. Lo menciono aquí ya que proporciona un razonamiento detrás de este enfoque sobre el simple uso de la publicación get_dummies (revelación: este es mi propio blog).

Aquí hay una solución que utiliza DictVectorizer y el DataFrame.to_dict('records') Pandas DataFrame.to_dict('records') .

 >>> import pandas as pd >>> X = pd.DataFrame({'income': [100000,110000,90000,30000,14000,50000], 'country':['US', 'CAN', 'US', 'CAN', 'MEX', 'US'], 'race':['White', 'Black', 'Latino', 'White', 'White', 'Black'] }) >>> from sklearn.feature_extraction import DictVectorizer >>> v = DictVectorizer() >>> qualitative_features = ['country','race'] >>> X_qual = v.fit_transform(X[qualitative_features].to_dict('records')) >>> v.vocabulary_ {'country=CAN': 0, 'country=MEX': 1, 'country=US': 2, 'race=Black': 3, 'race=Latino': 4, 'race=White': 5} >>> X_qual.toarray() array([[ 0., 0., 1., 0., 0., 1.], [ 1., 0., 0., 1., 0., 0.], [ 0., 0., 1., 0., 1., 0.], [ 1., 0., 0., 0., 0., 1.], [ 0., 1., 0., 0., 0., 1.], [ 0., 0., 1., 1., 0., 0.]]) 

pandas tiene la función incorporada “get_dummies” para obtener una encoding activa de esa / s columna / s en particular.

Un código de línea para una encoding en caliente:

 df=pd.concat([df,pd.get_dummies(df['column name'],prefix='column name')],axis=1).drop(['column name'],axis=1) 

Sé que llego tarde a esta fiesta, pero la forma más sencilla de codificar en caliente un dataframe de forma automatizada es usar esta función:

 def hot_encode(df): obj_df = df.select_dtypes(include=['object']) return pd.get_dummies(df, columns=obj_df.columns).values 

Puede pasar los datos al clasificador catboost sin encoding. Catboost maneja las variables categóricas por sí mismo al realizar una encoding de media y de expansión de destino.

Para agregar a otras preguntas, permítame proporcionarle cómo lo hice con una función Python 2.0 usando Numpy:

 def one_hot(y_): # Function to encode output labels from number indexes # eg: [[5], [0], [3]] --> [[0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0]] y_ = y_.reshape(len(y_)) n_values = np.max(y_) + 1 return np.eye(n_values)[np.array(y_, dtype=np.int32)] # Returns FLOATS 

La línea n_values = np.max(y_) + 1 podría tener una encoding rígida para que usted use el buen número de neuronas en caso de que use mini lotes, por ejemplo.

Proyecto de demostración / tutorial donde se ha utilizado esta función: https://github.com/guillaume-chevalier/LSTM-Human-Activity-Recognition

Esto funciona para mí:

 pandas.factorize( ['B', 'C', 'D', 'B'] )[0] 

Salida:

 [0, 1, 2, 0] 

Utilicé esto en mi modelo acústico: probablemente esto ayude en su modelo.

 def one_hot_encoding(x, n_out): x = x.astype(int) shape = x.shape x = x.flatten() N = len(x) x_categ = np.zeros((N,n_out)) x_categ[np.arange(N), x] = 1 return x_categ.reshape((shape)+(n_out,)) 

Aquí probé con este enfoque:

 import numpy as np #converting to one_hot def one_hot_encoder(value, datal): datal[value] = 1 return datal def _one_hot_values(labels_data): encoded = [0] * len(labels_data) for j, i in enumerate(labels_data): max_value = [0] * (np.max(labels_data) + 1) encoded[j] = one_hot_encoder(i, max_value) return np.array(encoded)