Codificación de etiquetas en varias columnas en scikit-learn
Estoy intentando usar scikit-learn LabelEncoder
para codificar pandas DataFrame
de etiquetas de cadenas. Como el marco de datos tiene muchas (50+) columnas, quiero evitar crear un LabelEncoder
objeto para cada columna; Prefiero tener un LabelEncoder
objeto grande que funcione en todas mis columnas de datos.
DataFrame
Lanzar todo LabelEncoder
crea el siguiente error. Tenga en cuenta que aquí estoy usando datos ficticios; 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 su 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)
Rastreo (última llamada más reciente): Archivo "", línea 1, en Archivo "/Users/bbalin/anaconda/lib/python2.7/site-packages/sklearn/preprocessing/label.py", línea 103, en forma y = column_or_1d(y, warn=True) Archivo "/Users/bbalin/anaconda/lib/python2.7/site-packages/sklearn/utils/validation.py", línea 306, en column_or_1d rise ValueError("forma de entrada incorrecta { 0}".format(shape)) ValueError: forma de entrada incorrecta (6, 3)
¿Alguna idea sobre cómo solucionar este problema?
Aunque puedes hacer esto fácilmente,
df.apply(LabelEncoder().fit_transform)
EDITAR2:
En scikit-learn 0.20, la forma recomendada es
OneHotEncoder().fit_transform(df)
ya que OneHotEncoder ahora admite la entrada de cadenas. Es posible aplicar OneHotEncoder solo a ciertas columnas con ColumnTransformer.
EDITAR:
Dado que esta respuesta original fue hace más de un año y generó muchos votos a favor (incluida una recompensa), probablemente debería extenderla más.
Para inverse_transform y transform, tienes que hacer un pequeño truco.
from collections import defaultdict
d = defaultdict(LabelEncoder)
Con esto, ahora conservas 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))
EDICIÓN DE MOAR:
Usando el paso de Neuraxle FlattenForEach
, también es posible hacer esto para usar el mismo LabelEncoder
en todos los datos aplanados a la vez:
FlattenForEach(LabelEncoder(), then_unflatten=True).fit_transform(df)
Para usar LabelEncoder
s separados dependiendo de sus columnas de datos, o si solo algunas de sus columnas de datos necesitan estar codificadas con etiquetas y otras no, entonces usar a es ColumnTransformer
una solución que permite un mayor control sobre su selección de columnas y sus instancias de LabelEncoder.
Como lo mencionó Larsmans, LabelEncoder() solo toma una matriz unidimensional como argumento . Dicho esto, es bastante fácil crear su propio codificador de etiquetas que opere en varias columnas de su elección y devuelva un marco de datos transformado. Mi código aquí se basa en parte en la excelente publicación del blog de Zac Stewart que se encuentra aquí .
Crear un codificador personalizado implica simplemente crear una clase que responda a los métodos fit()
, transform()
y fit_transform()
. En tu 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 weight
solo el atributo numérico. Podríamos hacer esto de la siguiente manera:
MultiColumnLabelEncoder(columns = ['fruit','color']).fit_transform(fruit_data)
Lo que transforma nuestro fruit_data
conjunto de datos de
a
Pasarle un marco de datos que consta enteramente de variables categóricas y omitir el columns
parámetro dará como resultado que se codifiquen todas las columnas (que creo que es lo que estaba buscando originalmente):
MultiColumnLabelEncoder().fit_transform(fruit_data.drop('weight',axis=1))
Esto transforma
a
.
Tenga en cuenta que probablemente se bloqueará cuando intente codificar atributos que ya son numéricos (agregue algo de código para manejar esto si lo desea).
Otra característica interesante 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)
Desde scikit-learn 0.20 puedes usar sklearn.compose.ColumnTransformer
y sklearn.preprocessing.OneHotEncoder
:
Si solo tienes variables categóricas, OneHotEncoder
directamente:
from sklearn.preprocessing import OneHotEncoder
OneHotEncoder(handle_unknown='ignore').fit_transform(df)
Si tiene funciones escritas de forma heterogénea:
from sklearn.compose import make_column_transformer
from sklearn.preprocessing import RobustScaler
from sklearn.preprocessing import OneHotEncoder
categorical_columns = ['pets', 'owner', 'location']
numerical_columns = ['age', 'weigth', 'height']
column_trans = make_column_transformer(
(categorical_columns, OneHotEncoder(handle_unknown='ignore'),
(numerical_columns, RobustScaler())
column_trans.fit_transform(df)
Más opciones en la documentación: http://scikit-learn.org/stable/modules/compose.html#columntransformer-for-heterogeneous-data