Codificação de etiquetas em várias colunas no scikit-learn

216

Estou tentando usar o scikit-learn's LabelEncoderpara codificar um panda DataFramede rótulos de string. Como o dataframe possui muitas (50+) colunas, desejo evitar a criação de um LabelEncoderobjeto para cada coluna; Prefiro ter apenas um grande LabelEncoderobjeto que funcione em todas as minhas colunas de dados.

Jogar o todo DataFramepara dentro LabelEncodercria o erro abaixo. Lembre-se de que estou usando dados fictícios aqui; na verdade, estou lidando com cerca de 50 colunas de dados rotulados com strings; portanto, preciso de uma solução que não faça referência a nenhuma coluna pelo nome.

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 (última chamada mais recente): arquivo "", linha 1, no arquivo "/Users/bbalin/anaconda/lib/python2.7/site-packages/sklearn/preprocessing/label.py", linha 103, em forma de = column_or_1d (y, warn = True) Arquivo "/Users/bbalin/anaconda/lib/python2.7/site-packages/sklearn/utils/validation.py", linha 306, na coluna 306, em column_or_1d aumento ValueError ("formato de entrada incorreto { 0} ". Formato (forma)) ValorErro: forma incorreta de entrada (6, 3)

Alguma idéia de como contornar esse problema?

Bryan
fonte
Por que você está tentando fazer isso?
Fred Foo
Para simplificar a codificação de uma coluna múltipla dataframede dados de sequência. Estou escolhendo o (s) objeto (s) de codificação, para evitar ter que selecionar / retirar 50 objetos separados. Além disso, gostaria de saber se existe uma maneira de o codificador simplificar os dados, ou seja, apenas retornar uma linha com um identificador para cada combinação única de variáveis ​​em cada coluna.
Bryan
Existe uma maneira simples de fazer tudo isso nos pandas, passando um dicionário de dicionários para o replacemétodo. Veja esta resposta abaixo
Ted Petrou

Respostas:

451

Você pode fazer isso facilmente,

df.apply(LabelEncoder().fit_transform)

EDIT2:

No scikit-learn 0.20, a maneira recomendada é

OneHotEncoder().fit_transform(df)

como o OneHotEncoder agora suporta entrada de string. A aplicação de OneHotEncoder somente a determinadas colunas é possível com o ColumnTransformer.

EDITAR:

Como essa resposta foi feita há mais de um ano e gerou muitos votos positivos (incluindo uma recompensa), eu provavelmente deveria estender isso ainda mais.

Para inverse_transform e transform, você precisa fazer um pouco de hack.

from collections import defaultdict
d = defaultdict(LabelEncoder)

Com isso, você agora mantém todas as colunas LabelEncodercomo dicionário.

# 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))
Napitupulu Jon
fonte
1
Isso é incrível, mas, neste caso, como podemos aplicar a transformação inversa?
Supreeth Meka
10
Mas se eu quiser usar esta solução em um pipeline, por exemplo, ajuste e transformação separados (ajuste no trem e depois no conjunto de teste -> reutilizar o dicionário aprendido), isso é suportado df.apply(LabelEncoder().fit_transform)?
Georg Heiler
2
Como isso pode ser feito para funcionar LabelBinarizere reutilizar o dicionário para um conjunto de testes? Eu tentei d = defaultdict(LabelBinarizer)e, em seguida, fit = df.apply(lambda x: d[x.name].fit_transform(x))mas uma exceção: Exception: Data must be 1-dimensional. Não tenho certeza de como espero que o DataFrame resultante pareça ... talvez cada coluna deva conter os vetores binarizados.
Qululu
4
Ótima solução. Como transformar apenas em determinada coluna?
31717 stenlytw
1
se eu quiser inverter o juste de codificação para uma coluna, como faço?
Ib D
95

Conforme mencionado por larsmans, LabelEncoder () usa apenas uma matriz 1-d como argumento . Dito isso, é muito fácil rolar seu próprio codificador de etiquetas que opera em várias colunas de sua escolha e retorna um quadro de dados transformado. Meu código aqui é baseado em parte no excelente post de Zac Stewart, encontrado aqui .

Criando um codificador personalizado envolve simplesmente criando uma classe que responde ao fit(), transform()e fit_transform()métodos. No seu caso, um bom começo pode ser algo assim:

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)

Suponha que desejemos codificar nossos dois atributos categóricos ( fruite color), deixando o atributo numérico em weightpaz. Poderíamos fazer o seguinte:

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

O que transforma nosso fruit_dataconjunto de dados de

insira a descrição da imagem aqui para

insira a descrição da imagem aqui

Passá-lo para um dataframe consistindo inteiramente de variáveis ​​categóricas e omitir o columnsparâmetro resultará na codificação de todas as colunas (que eu acredito ser o que você estava procurando originalmente):

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

Isso transforma

insira a descrição da imagem aqui para

insira a descrição da imagem aqui.

Observe que provavelmente ele engasgará quando tentar codificar atributos que já são numéricos (adicione algum código para lidar com isso, se quiser).

Outro recurso interessante sobre isso é que podemos usar esse transformador personalizado em um pipeline:

encoding_pipeline = Pipeline([
    ('encoding',MultiColumnLabelEncoder(columns=['fruit','color']))
    # add more pipeline steps as needed
])
encoding_pipeline.fit_transform(fruit_data)
PriceHardman
fonte
2
Acabei de perceber que os dados indicam que uma laranja é colorida de verde. Opa ;)
PriceHardman
5
essa é uma boa maneira de transformar dados uma vez, mas e se eu quiser reutilizar essa transformação em um conjunto de validação. você precisaria ajustar_transform novamente e poderão surgir problemas, como meu novo conjunto de dados sem todas as categorias para todas as variáveis. por exemplo, digamos que a cor verde não apareça no meu novo conjunto de dados. isso vai atrapalhar a codificação.
Ben
3
Concordou com @Ben. Na verdade, isso não imita o sklearn além dos nomes dos métodos. Se você tentou colocar isso em um Pipeline, que não iria funcionar
Tgsmith61591
3
Para garantir que a codificação de etiquetas seja consistente nos conjuntos de treinamento e teste, você deverá executar a codificação em todo o conjunto de dados (treinamento + teste). Isso pode ser feito antes de você os dividir em treinamento e teste, ou você pode combiná-los, executar a codificação e dividi-los novamente.
PriceHardman
2
Que tal ir ao contrário? decodificando de volta ao original?
user702846
18

Desde o scikit-learn 0.20, você pode usar sklearn.compose.ColumnTransformere sklearn.preprocessing.OneHotEncoder:

Se você tiver apenas variáveis ​​categóricas, OneHotEncoderdiretamente:

from sklearn.preprocessing import OneHotEncoder

OneHotEncoder(handle_unknown='ignore').fit_transform(df)

Se você tiver recursos de tipo heterogêneo:

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)

Mais opções na documentação: http://scikit-learn.org/stable/modules/compose.html#columntransformer-for-heterogeneous-data

ogrisel
fonte
inverse_transform()não é suportado no ColumnTransformer. Pelo menos, não no momento: github.com/scikit-learn/scikit-learn/issues/11463 . Essa é uma grande desvantagem para meu aplicativo e provavelmente também será para outros.
Sander Vanden Hautte
16

Não precisamos de um LabelEncoder.

Você pode converter as colunas em categorias e obter seus códigos. Usei uma compreensão de dicionário abaixo para aplicar esse processo a todas as colunas e agrupar o resultado novamente em um dataframe da mesma forma com índices e nomes de colunas idênticos.

>>> 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 criar um dicionário de mapeamento, basta enumerar as categorias usando uma compreensão de dicionário:

>>> {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'}}
Alexander
fonte
Se eu quiser voltar (reverter) para uma coluna (variável de destino de exemplo: Y), como faço?
Ib D
9

isso não responde diretamente à sua pergunta (para a qual Naputipulu Jon e PriceHardman têm respostas fantásticas)

No entanto, para o propósito de algumas tarefas de classificação, etc., você pode usar

pandas.get_dummies(input_df) 

isso pode inserir o quadro de dados com dados categóricos e retornar um quadro de dados com valores binários. valores de variáveis ​​são codificados em nomes de colunas no quadro de dados resultante. Mais

Anurag Priyadarshi
fonte
6

Supondo que você esteja simplesmente tentando obter um sklearn.preprocessing.LabelEncoder()objeto que possa ser usado para representar suas colunas, tudo que você precisa fazer é:

le.fit(df.columns)

No código acima, você terá um número único correspondente a cada coluna. Mais precisamente, você terá um mapeamento de 1: 1 df.columnspara le.transform(df.columns.get_values()). Para obter a codificação de uma coluna, simplesmente passe-a para le.transform(...). Como exemplo, o seguinte obterá a codificação para cada coluna:

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

Supondo que você deseja criar um sklearn.preprocessing.LabelEncoder()objeto para todos os seus rótulos de linha, faça o seguinte:

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

Nesse caso, é provável que você tenha rótulos de linha não exclusivos (como mostrado na sua pergunta). Para ver quais classes o codificador criou, você pode fazer le.classes_. Você notará que isso deve ter os mesmos elementos que em set(y for x in df.get_values() for y in x). Mais uma vez, para converter um rótulo de linha em um rótulo codificado, use le.transform(...). Como exemplo, se você deseja recuperar o rótulo para a primeira coluna na df.columnsmatriz e a primeira linha, você pode fazer o seguinte:

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

A pergunta que você teve no seu comentário é um pouco mais complicada, mas ainda pode ser realizada:

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

O código acima faz o seguinte:

  1. Faça uma combinação única de todos os pares de (coluna, linha)
  2. Represente cada par como uma versão em cadeia da tupla. Esta é uma solução alternativa para superar a LabelEncoderclasse que não suporta tuplas como um nome de classe.
  3. Ajusta os novos itens ao LabelEncoder.

Agora, para usar esse novo modelo, é um pouco mais complicado. Supondo que desejamos extrair a representação para o mesmo item procurado no exemplo anterior (a primeira coluna em df.columns e a primeira linha), podemos fazer isso:

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

Lembre-se de que cada pesquisa agora é uma representação em cadeia de uma tupla que contém a (coluna, linha).

TehTechGuy
fonte
5

Não, LabelEncodernão faz isso. Leva matrizes 1-d de rótulos de classe e produz matrizes 1-d. Ele foi projetado para lidar com rótulos de classe em problemas de classificação, não em dados arbitrários, e qualquer tentativa de forçá-lo a outros usos exigirá código para transformar o problema real no problema que resolve (e a solução de volta ao espaço original).

Fred Foo
fonte
Ok, considerando isso, qual é a sua sugestão sobre a melhor maneira de codificar rótulos de string por um inteiro de DataFramecada vez?
Bryan
@Bryan Veja o LabelEncodercódigo e adapte-o. Eu não uso o Pandas, então não sei o quão difícil será.
Fred Foo
Também deixarei que outras pandaspessoas resolvam essa questão - tenho certeza de que não sou a única pessoa com esse desafio, por isso espero que exista uma solução pré-criada por aí.
Bryan
5

Isso aconteceu um ano e meio após o fato, mas eu também precisava ser capaz de .transform()múltiplas colunas de dataframe de pandas de uma só vez (e ser capaz .inverse_transform()delas também). Isso expande a excelente sugestão do @PriceHardman acima:

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.loc[:, self.columns].values

    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.loc[:, self.columns].values

Exemplo:

Se dfe df_copy()são pandasquadros de dados de tipo misto, você pode aplicar MultiColumnLabelEncoder()as dtype=objectcolunas da seguinte maneira:

# 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)

Você pode acessar classes de colunas individuais, rótulos de colunas e codificadores de colunas usados ​​para caber em cada coluna por meio de indexação:

mcle.all_classes_
mcle.all_encoders_
mcle.all_labels_

Jason Wolosonovich
fonte
Olá Jason, mcle.all_labels_ não parece funcionar (Python 3.5, Conda 4.3.29, Sklearn 0.18.1, Pandas 0.20.1. Eu recebo: AttributeError: 'MultiColumnLabelEncoder' o objeto não tem atributo 'all_labels_'
Jason
@ Jason Oi, desculpe, eu não vi isso até hoje: / mas se eu tivesse que adivinhar, eu diria que você acabou de usar o fitmétodo acima, que na verdade não produzirá nenhum rótulo até aplicá-lo ( transform/ fit_transform) a os dados.
Jason Wolosonovich
Acho que você precisa dar um exemplo melhor - não consegui executar novamente todos os seus códigos.
user702846
2

Seguindo os comentários levantados sobre a solução do @PriceHardman , proponho a seguinte versão da classe:

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 classe encaixa o codificador no conjunto de treinamento e usa a versão ajustada ao transformar. A versão inicial do código pode ser encontrada aqui .

Dror
fonte
2

Um caminho curto para LabelEncoder()várias colunas com um 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])

e você pode usar isso le_dictpara labelEncode qualquer outra coluna:

le_dict[col].transform(df_another[col])
Tom
fonte
2

É possível fazer isso tudo diretamente nos pandas e é adequado para uma habilidade exclusiva do replacemétodo.

Primeiro, vamos criar um dicionário de dicionários mapeando as colunas e seus valores para seus novos valores de substituição.

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 esse sempre será um mapeamento individual, podemos inverter o dicionário interno para obter um mapeamento dos novos valores de volta ao 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'}}

Agora, podemos usar a capacidade exclusiva do replacemétodo de obter uma lista aninhada de dicionários e usar as chaves externas como colunas e as chaves internas como os valores que gostaríamos de substituir.

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 voltar facilmente ao original encadeando novamente o replacemétodo

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
Ted Petrou
fonte
2

Depois de muita pesquisa e experimentação com algumas respostas aqui e em outros lugares, acho que sua resposta está aqui :

pd.DataFrame (colunas = df.columns, data = LabelEncoder (). fit_transform (df.values.flatten ()). reshape (df.shape))

Isso preservará os nomes das categorias nas colunas:

import pandas as pd
from sklearn.preprocessing import LabelEncoder

df = pd.DataFrame([['A','B','C','D','E','F','G','I','K','H'],
                   ['A','E','H','F','G','I','K','','',''],
                   ['A','C','I','F','H','G','','','','']], 
                  columns=['A1', 'A2', 'A3','A4', 'A5', 'A6', 'A7', 'A8', 'A9', 'A10'])

pd.DataFrame(columns=df.columns, data=LabelEncoder().fit_transform(df.values.flatten()).reshape(df.shape))

    A1  A2  A3  A4  A5  A6  A7  A8  A9  A10
0   1   2   3   4   5   6   7   9   10  8
1   1   5   8   6   7   9   10  0   0   0
2   1   3   9   6   8   7   0   0   0   0
Christopher
fonte
2

Eu verifiquei o código fonte ( https://github.com/scikit-learn/scikit-learn/blob/master/sklearn/preprocessing/label.py ) do LabelEncoder. Foi baseado em um conjunto de transformação numpy, que é uma delas np.unique (). E essa função recebe apenas entrada de matriz 1-d. (corrija-me se eu estiver errada).

Idéias muito difíceis ... primeiro, identifique quais colunas precisam do LabelEncoder e, em seguida, faça um loop por cada coluna.

def cat_var(df): 
    """Identify categorical features. 

    Parameters
    ----------
    df: original df after missing operations 

    Returns
    -------
    cat_var_df: summary df with col index and col name for all categorical vars
    """
    col_type = df.dtypes
    col_names = list(df)

    cat_var_index = [i for i, x in enumerate(col_type) if x=='object']
    cat_var_name = [x for i, x in enumerate(col_names) if i in cat_var_index]

    cat_var_df = pd.DataFrame({'cat_ind': cat_var_index, 
                               'cat_name': cat_var_name})

    return cat_var_df



from sklearn.preprocessing import LabelEncoder 

def column_encoder(df, cat_var_list):
    """Encoding categorical feature in the dataframe

    Parameters
    ----------
    df: input dataframe 
    cat_var_list: categorical feature index and name, from cat_var function

    Return
    ------
    df: new dataframe where categorical features are encoded
    label_list: classes_ attribute for all encoded features 
    """

    label_list = []
    cat_var_df = cat_var(df)
    cat_list = cat_var_df.loc[:, 'cat_name']

    for index, cat_feature in enumerate(cat_list): 

        le = LabelEncoder()

        le.fit(df.loc[:, cat_feature])    
        label_list.append(list(le.classes_))

        df.loc[:, cat_feature] = le.transform(df.loc[:, cat_feature])

    return df, label_list

O df retornado seria o após a codificação, e label_list mostrará o que todos esses valores significam na coluna correspondente. Este é um trecho de um script de processo de dados que escrevi para o trabalho. Deixe-me saber se você acha que poderia haver mais melhorias.

Edição: Só quero mencionar aqui que os métodos acima funcionam com quadro de dados sem perder o melhor. Não tenho certeza de como ele está trabalhando no quadro de dados contém dados ausentes. (Eu tinha um acordo com o procedimento ausente antes de executar os métodos acima)

willaccc
fonte
1

se tivermos uma coluna para codificar o rótulo e sua transformação inversa, será fácil como fazê-lo quando houver várias colunas em 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
Puja Sharma
fonte
1

Se você possui os dois tipos de dados numéricos e categóricos no dataframe Você pode usar: aqui X é o meu dataframe com as duas variáveis ​​categóricas e 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: Essa técnica é boa se você não estiver interessado em convertê-las novamente.

Vikas Gupta
fonte
1

Usando Neuraxle

TLDR; Você aqui pode usar o FlattenForEach invólucro de classe para simplesmente transformar sua df como: FlattenForEach(LabelEncoder(), then_unflatten=True).fit_transform(df).

Com esse método, seu codificador de etiquetas poderá se ajustar e se transformar em um pipeline de aprendizado de scikit regular . Vamos simplesmente importar:

from sklearn.preprocessing import LabelEncoder
from neuraxle.steps.column_transformer import ColumnTransformer
from neuraxle.steps.loop import FlattenForEach

O mesmo codificador compartilhado para colunas:

Aqui está como um LabelEncoder compartilhado será aplicado a todos os dados para codificá-lo:

    p = FlattenForEach(LabelEncoder(), then_unflatten=True)

Resultado:

    p, predicted_output = p.fit_transform(df.values)
    expected_output = np.array([
        [6, 7, 6, 8, 7, 7],
        [1, 3, 0, 1, 5, 3],
        [4, 2, 2, 4, 4, 2]
    ]).transpose()
    assert np.array_equal(predicted_output, expected_output)

Diferentes codificadores por coluna:

E aqui está como um primeiro LabelEncoder autônomo será aplicado nos animais de estimação e um segundo será compartilhado para o proprietário e o local das colunas. Para ser mais preciso, aqui temos uma mistura de codificadores de etiqueta diferentes e compartilhados:

    p = ColumnTransformer([
        # A different encoder will be used for column 0 with name "pets":
        (0, FlattenForEach(LabelEncoder(), then_unflatten=True)),
        # A shared encoder will be used for column 1 and 2, "owner" and "location":
        ([1, 2], FlattenForEach(LabelEncoder(), then_unflatten=True)),
    ], n_dimension=2)

Resultado:

    p, predicted_output = p.fit_transform(df.values)
    expected_output = np.array([
        [0, 1, 0, 2, 1, 1],
        [1, 3, 0, 1, 5, 3],
        [4, 2, 2, 4, 4, 2]
    ]).transpose()
    assert np.array_equal(predicted_output, expected_output)
Guillaume Chevalier
fonte
0

Utilizou a resposta @Alexander principalmente, mas teve que fazer algumas alterações -

cols_need_mapped = ['col1', 'col2']

mapper = {col: {cat: n for n, cat in enumerate(df[col].astype('category').cat.categories)} 
     for col in df[cols_need_mapped]}

for c in cols_need_mapped :
    df[c] = df[c].map(mapper[c])

Depois, para reutilizar no futuro, você pode salvar a saída em um documento json e, quando precisar, lê-la e usar a .map()função como fiz acima.

bbennett36
fonte
0

O problema é a forma dos dados (pd dataframe) que você está passando para a função de ajuste. Você precisa passar na 1ª lista.

Ali Sadr
fonte
0
import pandas as pd
from sklearn.preprocessing import LabelEncoder

train=pd.read_csv('.../train.csv')

#X=train.loc[:,['waterpoint_type_group','status','waterpoint_type','source_class']].values
# Create a label encoder object 
def MultiLabelEncoder(columnlist,dataframe):
    for i in columnlist:

        labelencoder_X=LabelEncoder()
        dataframe[i]=labelencoder_X.fit_transform(dataframe[i])
columnlist=['waterpoint_type_group','status','waterpoint_type','source_class','source_type']
MultiLabelEncoder(columnlist,train)

Aqui estou lendo um csv do local e, em função, estou passando a lista de colunas que quero codificar e o quadro de dados que quero aplicar.


fonte
0

Que tal agora?

def MultiColumnLabelEncode(choice, columns, X):
    LabelEncoders = []
    if choice == 'encode':
        for i in enumerate(columns):
            LabelEncoders.append(LabelEncoder())
        i=0    
        for cols in columns:
            X[:, cols] = LabelEncoders[i].fit_transform(X[:, cols])
            i += 1
    elif choice == 'decode': 
        for cols in columns:
            X[:, cols] = LabelEncoders[i].inverse_transform(X[:, cols])
            i += 1
    else:
        print('Please select correct parameter "choice". Available parameters: encode/decode')

Não é o mais eficiente, no entanto, funciona e é super simples.

Dominik Novotný
fonte