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

python - sklearn - Etiquetar la codificación en múltiples columnas en scikit-learn



onehotencoder onehotencoder categorical_features 1]) (12)

Intento usar LabelEncoder de scikit-learn para codificar un DataFrame de DataFrame de pandas de etiquetas de cadena. Como el marco de datos tiene muchas (más de) columnas, quiero evitar crear un objeto LabelEncoder para cada columna; Prefiero tener solo un gran objeto LabelEncoder que funcione en todas mis columnas de datos.

Al DataFrame todo el DataFrame en LabelEncoder crea el siguiente error. Tenga en cuenta que estoy usando datos ficticios aquí; en realidad, estoy tratando con alrededor de 50 columnas de datos etiquetados con cadenas, por lo que necesito una solución que no haga referencia a ninguna columna por nombre.

import pandas from sklearn import preprocessing df = pandas.DataFrame({''pets'':[''cat'', ''dog'', ''cat'', ''monkey'', ''dog'', ''dog''], ''owner'':[''Champ'', ''Ron'', ''Brick'', ''Champ'', ''Veronica'', ''Ron''], ''location'':[''San_Diego'', ''New_York'', ''New_York'', ''San_Diego'', ''San_Diego'', ''New_York'']}) le = preprocessing.LabelEncoder() le.fit(df)

Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/Users/bbalin/anaconda/lib/python2.7/site-packages/sklearn/preprocessing/label.py", line 103, in fit y = column_or_1d(y, warn=True) File "/Users/bbalin/anaconda/lib/python2.7/site-packages/sklearn/utils/validation.py", line 306, in column_or_1d raise ValueError("bad input shape {0}".format(shape)) ValueError: bad input shape (6, 3)

¿Alguna idea sobre cómo solucionar este problema?


Como lo menciona larsmans, LabelEncoder () solo toma una matriz de 1-d como argumento . Dicho esto, es bastante fácil rodar su propio codificador de etiquetas que opera en varias columnas de su elección, y devuelve un marco de datos transformado. Mi código aquí se basa en parte en la excelente publicación de blog de Zac Stewart que se encuentra here .

Crear un codificador personalizado implica simplemente crear una clase que responda a los métodos fit() , transform() y fit_transform() . En su caso, un buen comienzo podría ser algo como esto:

import pandas as pd from sklearn.preprocessing import LabelEncoder from sklearn.pipeline import Pipeline # Create some toy data in a Pandas dataframe fruit_data = pd.DataFrame({ ''fruit'': [''apple'',''orange'',''pear'',''orange''], ''color'': [''red'',''orange'',''green'',''green''], ''weight'': [5,6,3,4] }) class MultiColumnLabelEncoder: def __init__(self,columns = None): self.columns = columns # array of column names to encode def fit(self,X,y=None): return self # not relevant here def transform(self,X): '''''' Transforms columns of X specified in self.columns using LabelEncoder(). If no columns specified, transforms all columns in X. '''''' output = X.copy() if self.columns is not None: for col in self.columns: output[col] = LabelEncoder().fit_transform(output[col]) else: for colname,col in output.iteritems(): output[colname] = LabelEncoder().fit_transform(col) return output def fit_transform(self,X,y=None): return self.fit(X,y).transform(X)

Supongamos que queremos codificar nuestros dos atributos categóricos ( fruit y color ), dejando solo el weight atributo numérico. Podríamos hacer esto de la siguiente manera:

MultiColumnLabelEncoder(columns = [''fruit'',''color'']).fit_transform(fruit_data)

Que transforma nuestro dataset fruit_data de

a

Pasarle un marco de datos que consista completamente en variables categóricas y omitir el parámetro de columns dará como resultado que cada columna esté codificada (que creo que es lo que originalmente estabas buscando):

MultiColumnLabelEncoder().fit_transform(fruit_data.drop(''weight'',axis=1))

Esto transforma

a

.

Tenga en cuenta que probablemente se ahogará cuando intente codificar atributos que ya son numéricos (agregue un código para manejar esto si lo desea).

Otra buena característica de esto es que podemos usar este transformador personalizado en una tubería:

encoding_pipeline = Pipeline([ (''encoding'',MultiColumnLabelEncoder(columns=[''fruit'',''color''])) # add more pipeline steps as needed ]) encoding_pipeline.fit_transform(fruit_data)


Es posible hacer esto todo en pandas directamente y es adecuado para una habilidad única del método de replace .

Primero, hagamos un diccionario de diccionarios mapeando las columnas y sus valores a sus nuevos valores de reemplazo.

transform_dict = {} for col in df.columns: cats = pd.Categorical(df[col]).categories d = {} for i, cat in enumerate(cats): d[cat] = i transform_dict[col] = d transform_dict {''location'': {''New_York'': 0, ''San_Diego'': 1}, ''owner'': {''Brick'': 0, ''Champ'': 1, ''Ron'': 2, ''Veronica'': 3}, ''pets'': {''cat'': 0, ''dog'': 1, ''monkey'': 2}}

Como esto siempre será un mapeo de uno a uno, podemos invertir el diccionario interno para obtener un mapeo de los nuevos valores de vuelta al original.

inverse_transform_dict = {} for col, d in transform_dict.items(): inverse_transform_dict[col] = {v:k for k, v in d.items()} inverse_transform_dict {''location'': {0: ''New_York'', 1: ''San_Diego''}, ''owner'': {0: ''Brick'', 1: ''Champ'', 2: ''Ron'', 3: ''Veronica''}, ''pets'': {0: ''cat'', 1: ''dog'', 2: ''monkey''}}

Ahora, podemos usar la capacidad única del método de replace para tomar una lista anidada de diccionarios y usar las claves externas como columnas, y las claves internas como los valores que nos gustaría reemplazar.

df.replace(transform_dict) location owner pets 0 1 1 0 1 0 2 1 2 0 0 0 3 1 1 2 4 1 3 1 5 0 2 1

Podemos volver fácilmente al original volviendo a encadenar el método de replace

df.replace(transform_dict).replace(inverse_transform_dict) location owner pets 0 San_Diego Champ cat 1 New_York Ron dog 2 New_York Brick cat 3 San_Diego Champ monkey 4 San_Diego Veronica dog 5 New_York Ron dog


Esto es un año y medio después del hecho, pero yo también necesitaba poder .transform() múltiples columnas de cuadros de datos de pandas a la vez (y también poder .inverse_transform() ). Esto amplía la excelente sugerencia de @PriceHardman anterior:

class MultiColumnLabelEncoder(LabelEncoder): """ Wraps sklearn LabelEncoder functionality for use on multiple columns of a pandas dataframe. """ def __init__(self, columns=None): self.columns = columns def fit(self, dframe): """ Fit label encoder to pandas columns. Access individual column classes via indexig `self.all_classes_` Access individual column encoders via indexing `self.all_encoders_` """ # if columns are provided, iterate through and get `classes_` if self.columns is not None: # ndarray to hold LabelEncoder().classes_ for each # column; should match the shape of specified `columns` self.all_classes_ = np.ndarray(shape=self.columns.shape, dtype=object) self.all_encoders_ = np.ndarray(shape=self.columns.shape, dtype=object) for idx, column in enumerate(self.columns): # fit LabelEncoder to get `classes_` for the column le = LabelEncoder() le.fit(dframe.loc[:, column].values) # append the `classes_` to our ndarray container self.all_classes_[idx] = (column, np.array(le.classes_.tolist(), dtype=object)) # append this column''s encoder self.all_encoders_[idx] = le else: # no columns specified; assume all are to be encoded self.columns = dframe.iloc[:, :].columns self.all_classes_ = np.ndarray(shape=self.columns.shape, dtype=object) for idx, column in enumerate(self.columns): le = LabelEncoder() le.fit(dframe.loc[:, column].values) self.all_classes_[idx] = (column, np.array(le.classes_.tolist(), dtype=object)) self.all_encoders_[idx] = le return self def fit_transform(self, dframe): """ Fit label encoder and return encoded labels. Access individual column classes via indexing `self.all_classes_` Access individual column encoders via indexing `self.all_encoders_` Access individual column encoded labels via indexing `self.all_labels_` """ # if columns are provided, iterate through and get `classes_` if self.columns is not None: # ndarray to hold LabelEncoder().classes_ for each # column; should match the shape of specified `columns` self.all_classes_ = np.ndarray(shape=self.columns.shape, dtype=object) self.all_encoders_ = np.ndarray(shape=self.columns.shape, dtype=object) self.all_labels_ = np.ndarray(shape=self.columns.shape, dtype=object) for idx, column in enumerate(self.columns): # instantiate LabelEncoder le = LabelEncoder() # fit and transform labels in the column dframe.loc[:, column] =/ le.fit_transform(dframe.loc[:, column].values) # append the `classes_` to our ndarray container self.all_classes_[idx] = (column, np.array(le.classes_.tolist(), dtype=object)) self.all_encoders_[idx] = le self.all_labels_[idx] = le else: # no columns specified; assume all are to be encoded self.columns = dframe.iloc[:, :].columns self.all_classes_ = np.ndarray(shape=self.columns.shape, dtype=object) for idx, column in enumerate(self.columns): le = LabelEncoder() dframe.loc[:, column] = le.fit_transform( dframe.loc[:, column].values) self.all_classes_[idx] = (column, np.array(le.classes_.tolist(), dtype=object)) self.all_encoders_[idx] = le return dframe def transform(self, dframe): """ Transform labels to normalized encoding. """ if self.columns is not None: for idx, column in enumerate(self.columns): dframe.loc[:, column] = self.all_encoders_[ idx].transform(dframe.loc[:, column].values) else: self.columns = dframe.iloc[:, :].columns for idx, column in enumerate(self.columns): dframe.loc[:, column] = self.all_encoders_[idx]/ .transform(dframe.loc[:, column].values) return dframe.loc[:, self.columns].values def inverse_transform(self, dframe): """ Transform labels back to original encoding. """ if self.columns is not None: for idx, column in enumerate(self.columns): dframe.loc[:, column] = self.all_encoders_[idx]/ .inverse_transform(dframe.loc[:, column].values) else: self.columns = dframe.iloc[:, :].columns for idx, column in enumerate(self.columns): dframe.loc[:, column] = self.all_encoders_[idx]/ .inverse_transform(dframe.loc[:, column].values) return dframe

Ejemplo:

Si df y df_copy() son dataframes de pandas tipo mixto, puede aplicar MultiColumnLabelEncoder() a las columnas dtype=object de la siguiente manera:

# get `object` columns df_object_columns = df.iloc[:, :].select_dtypes(include=[''object'']).columns df_copy_object_columns = df_copy.iloc[:, :].select_dtypes(include=[''object''].columns # instantiate `MultiColumnLabelEncoder` mcle = MultiColumnLabelEncoder(columns=object_columns) # fit to `df` data mcle.fit(df) # transform the `df` data mcle.transform(df) # returns output like below array([[1, 0, 0, ..., 1, 1, 0], [0, 5, 1, ..., 1, 1, 2], [1, 1, 1, ..., 1, 1, 2], ..., [3, 5, 1, ..., 1, 1, 2], # transform `df_copy` data mcle.transform(df_copy) # returns output like below (assuming the respective columns # of `df_copy` contain the same unique values as that particular # column in `df` array([[1, 0, 0, ..., 1, 1, 0], [0, 5, 1, ..., 1, 1, 2], [1, 1, 1, ..., 1, 1, 2], ..., [3, 5, 1, ..., 1, 1, 2], # inverse `df` data mcle.inverse_transform(df) # outputs data like below array([[''August'', ''Friday'', ''2013'', ..., ''N'', ''N'', ''CA''], [''April'', ''Tuesday'', ''2014'', ..., ''N'', ''N'', ''NJ''], [''August'', ''Monday'', ''2014'', ..., ''N'', ''N'', ''NJ''], ..., [''February'', ''Tuesday'', ''2014'', ..., ''N'', ''N'', ''NJ''], [''April'', ''Tuesday'', ''2014'', ..., ''N'', ''N'', ''NJ''], [''March'', ''Tuesday'', ''2013'', ..., ''N'', ''N'', ''NJ'']], dtype=object) # inverse `df_copy` data mcle.inverse_transform(df_copy) # outputs data like below array([[''August'', ''Friday'', ''2013'', ..., ''N'', ''N'', ''CA''], [''April'', ''Tuesday'', ''2014'', ..., ''N'', ''N'', ''NJ''], [''August'', ''Monday'', ''2014'', ..., ''N'', ''N'', ''NJ''], ..., [''February'', ''Tuesday'', ''2014'', ..., ''N'', ''N'', ''NJ''], [''April'', ''Tuesday'', ''2014'', ..., ''N'', ''N'', ''NJ''], [''March'', ''Tuesday'', ''2013'', ..., ''N'', ''N'', ''NJ'']], dtype=object)

Puede acceder a clases de columnas individuales, etiquetas de columnas y codificadores de columnas utilizados para ajustarse a cada columna a través de la indexación:

mcle.all_classes_
mcle.all_encoders_
mcle.all_labels_


No necesitamos un LabelEncoder.

Puede convertir las columnas en categorías y luego obtener sus códigos. Usé una comprensión de diccionario a continuación para aplicar este proceso a cada columna y envolver el resultado en un marco de datos de la misma forma con índices idénticos y nombres de columna.

>>> pd.DataFrame({col: df[col].astype(''category'').cat.codes for col in df}, index=df.index) location owner pets 0 1 1 0 1 0 2 1 2 0 0 0 3 1 1 2 4 1 3 1 5 0 2 1

Para crear un diccionario de mapeo, puede enumerar las categorías usando una comprensión de diccionario:

>>> {col: {n: cat for n, cat in enumerate(df[col].astype(''category'').cat.categories)} for col in df} {''location'': {0: ''New_York'', 1: ''San_Diego''}, ''owner'': {0: ''Brick'', 1: ''Champ'', 2: ''Ron'', 3: ''Veronica''}, ''pets'': {0: ''cat'', 1: ''dog'', 2: ''monkey''}}


No, LabelEncoder no hace esto. Toma arrays de 1-d de etiquetas de clase y produce matrices de 1-d. Está diseñado para manejar etiquetas de clase en problemas de clasificación, no datos arbitrarios, y cualquier intento de forzarlo para otros usos requerirá que el código transforme el problema real al problema que resuelve (y la solución vuelva al espacio original).


Si tiene datos numéricos y categóricos de ambos tipos de datos en el marco de datos Puede usar: aquí X es mi marco de datos con las dos variables categóricas y numéricas

from sklearn import preprocessing le = preprocessing.LabelEncoder() for i in range(0,X.shape[1]): if X.dtypes[i]==''object'': X[X.columns[i]] = le.fit_transform(X[X.columns[i]])

Nota: Esta técnica es buena si no está interesado en convertirlos de nuevo.


Siguiendo los comentarios sobre la solución de @PriceHardman , propondría la siguiente versión de la clase:

class LabelEncodingColoumns(BaseEstimator, TransformerMixin): def __init__(self, cols=None): pdu._is_cols_input_valid(cols) self.cols = cols self.les = {col: LabelEncoder() for col in cols} self._is_fitted = False def transform(self, df, **transform_params): """ Scaling ``cols`` of ``df`` using the fitting Parameters ---------- df : DataFrame DataFrame to be preprocessed """ if not self._is_fitted: raise NotFittedError("Fitting was not preformed") pdu._is_cols_subset_of_df_cols(self.cols, df) df = df.copy() label_enc_dict = {} for col in self.cols: label_enc_dict[col] = self.les[col].transform(df[col]) labelenc_cols = pd.DataFrame(label_enc_dict, # The index of the resulting DataFrame should be assigned and # equal to the one of the original DataFrame. Otherwise, upon # concatenation NaNs will be introduced. index=df.index ) for col in self.cols: df[col] = labelenc_cols[col] return df def fit(self, df, y=None, **fit_params): """ Fitting the preprocessing Parameters ---------- df : DataFrame Data to use for fitting. In many cases, should be ``X_train``. """ pdu._is_cols_subset_of_df_cols(self.cols, df) for col in self.cols: self.les[col].fit(df[col]) self._is_fitted = True return self

Esta clase se ajusta al codificador en el conjunto de entrenamiento y usa la versión ajustada cuando se transforma. La versión inicial del código se puede encontrar here .


Sin embargo, puedes hacer esto fácilmente,

df.apply(LabelEncoder().fit_transform)

EDITAR:

Dado que esta respuesta es hace más de un año, y generó muchos votos ascendentes (incluida una recompensa), probablemente debería extender esto más.

Para inverse_transform y transform, tienes que hacer un poco de hack.

from collections import defaultdict d = defaultdict(LabelEncoder)

Con esto, ahora conserva todas las columnas LabelEncoder como diccionario.

# Encoding the variable fit = df.apply(lambda x: d[x.name].fit_transform(x)) # Inverse the encoded fit.apply(lambda x: d[x.name].inverse_transform(x)) # Using the dictionary to label future data df.apply(lambda x: d[x.name].transform(x))


Suponiendo que simplemente intenta obtener un objeto sklearn.preprocessing.LabelEncoder() que se puede usar para representar sus columnas, todo lo que tiene que hacer es:

le.fit(df.columns)

En el código anterior, tendrá un número único correspondiente a cada columna. Más precisamente, tendrá una asignación de 1: 1 de df.columns a le.transform(df.columns.get_values()) . Para obtener la codificación de una columna, simplemente le.transform(...) a le.transform(...) . Como ejemplo, lo siguiente obtendrá la codificación para cada columna:

le.transform(df.columns.get_values())

Suponiendo que desea crear un objeto sklearn.preprocessing.LabelEncoder() para todas sus etiquetas de fila, puede hacer lo siguiente:

le.fit([y for x in df.get_values() for y in x])

En este caso, lo más probable es que tenga etiquetas de fila no exclusivas (como se muestra en su pregunta). Para ver qué clases creó el codificador, puede hacer le.classes_ . set(y for x in df.get_values() for y in x) que esto debería tener los mismos elementos que en set(y for x in df.get_values() for y in x) . Una vez más, para convertir una etiqueta de fila en una etiqueta codificada, use le.transform(...) . Como ejemplo, si desea recuperar la etiqueta de la primera columna en la matriz df.columns y la primera fila, puede hacer esto:

le.transform([df.get_value(0, df.columns[0])])

La pregunta que tenía en su comentario es un poco más complicada, pero aún se puede lograr:

le.fit([str(z) for z in set((x[0], y) for x in df.iteritems() for y in x[1])])

El código anterior hace lo siguiente:

  1. Haz una combinación única de todos los pares de (columna, fila)
  2. Represente cada par como una versión de cadena de la tupla. Esta es una solución para superar la clase LabelEncoder no admite tuplas como nombre de clase.
  3. Se adapta a los nuevos elementos en LabelEncoder .

Ahora usar este nuevo modelo es un poco más complicado. Suponiendo que queremos extraer la representación para el mismo artículo que buscamos en el ejemplo anterior (la primera columna en df.columns y la primera fila), podemos hacer esto:

le.transform([str((df.columns[0], df.get_value(0, df.columns[0])))])

Recuerde que cada búsqueda ahora es una representación de cadena de una tupla que contiene la (columna, fila).


Una breve forma de LabelEncoder() columnas múltiples con un dict() :

from sklearn.preprocessing import LabelEncoder le_dict = {col: LabelEncoder() for col in columns } for col in columns: le_dict[col].fit_transform(df[col])

y puede usar este le_dict para etiquetar cualquier otra columna enCódigo

le_dict[col].transform(df_another[col])


esto no responde directamente a su pregunta (para lo cual Naputipulu Jon y PriceHardman tienen respuestas fantásticas)

Sin embargo, con el propósito de algunas tareas de clasificación, etc. podrías usar

pandas.get_dummies(input_df)

esto puede ingresar dataframe con datos categóricos y devolver un dataframe con valores binarios. los valores variables se codifican en nombres de columna en el marco de datos resultante. more


si tenemos una sola columna para hacer la codificación de la etiqueta y su transformación inversa es fácil cómo hacerlo cuando hay múltiples columnas en python

def stringtocategory(dataset): '''''' @author puja.sharma @see The function label encodes the object type columns and gives label encoded and inverse tranform of the label encoded data @param dataset dataframe on whoes column the label encoding has to be done @return label encoded and inverse tranform of the label encoded data. '''''' data_original = dataset[:] data_tranformed = dataset[:] for y in dataset.columns: #check the dtype of the column object type contains strings or chars if (dataset[y].dtype == object): print("The string type features are : " + y) le = preprocessing.LabelEncoder() le.fit(dataset[y].unique()) #label encoded data data_tranformed[y] = le.transform(dataset[y]) #inverse label transform data data_original[y] = le.inverse_transform(data_tranformed[y]) return data_tranformed,data_original