Etichetta la codifica su più colonne in scikit-learn


218

Sto cercando di usare Scikit-Learn LabelEncoderper codificare un panda DataFramedi etichette di stringa. Poiché il dataframe ha molte (50+) colonne, voglio evitare di creare un LabelEncoderoggetto per ogni colonna; Preferirei avere solo un LabelEncoderoggetto grande che funzioni su tutte le mie colonne di dati.

Lanciare l'intero DataFramein LabelEncodercrea l'errore di seguito. Tieni presente che sto usando dati fittizi qui; in realtà ho a che fare con circa 50 colonne di dati con etichetta stringa, quindi ho bisogno di una soluzione che non faccia riferimento a nessuna colonna per 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 (ultima chiamata più recente): file "", riga 1, nel file "/Users/bbalin/anaconda/lib/python2.7/site-packages/sklearn/preprocessing/label.py", riga 103, in fit y = column_or_1d (y, warn = True) File "/Users/bbalin/anaconda/lib/python2.7/site-packages/sklearn/utils/validation.py", riga 306, in column_or_1d aumentare ValueError ("forma di input errata { 0} ". Format (forma)) ValueError: cattiva forma di input (6, 3)

Qualche idea su come aggirare questo problema?


Perché stai cercando di farlo?
Fred Foo,

Per semplificare la codifica di una colonna multipla dataframedi dati stringa. Sto ciclando gli oggetti di codifica, quindi voglio evitare di dover decapare / scartare 50 oggetti separati. Inoltre, mi chiedo se c'è un modo per consentire all'encoder di semplificare i dati, ovvero restituire una riga con un identificatore per ogni combinazione unica di variabili in ogni colonna.
Bryan,

C'è un modo semplice per fare tutto questo in panda passando un dizionario di dizionari al replacemetodo. Vedi questa risposta di seguito
Ted Petrou,

Risposte:


453

Puoi facilmente farlo però,

df.apply(LabelEncoder().fit_transform)

EDIT2:

In scikit-learn 0.20, il modo consigliato è

OneHotEncoder().fit_transform(df)

poiché OneHotEncoder ora supporta l'input di stringhe. L'applicazione di OneHotEncoder solo a determinate colonne è possibile con ColumnTransformer.

MODIFICARE:

Poiché questa risposta è avvenuta oltre un anno fa e ha generato molti voti (incluso un premio), probabilmente dovrei estenderlo ulteriormente.

Per inverse_transform e trasformazione, devi fare un po 'di hack.

from collections import defaultdict
d = defaultdict(LabelEncoder)

Con questo, ora conservi tutte le colonne LabelEncodercome dizionario.

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

1
Questo è incredibile, ma in questo caso come possiamo applicare la trasformazione inversa?
Supreeth Meka,

10
Ma se voglio usare questa soluzione in una pipeline, ad es. Adattamento e trasformazione separati (adattamento sul treno e quindi utilizzo su set di test -> riutilizzare il dizionario appreso) è supportato df.apply(LabelEncoder().fit_transform)?
Georg Heiler,

2
In che modo è possibile farlo funzionare LabelBinarizerinvece e riutilizzare il dizionario per un set di test? Ho provato d = defaultdict(LabelBinarizer)e poi fit = df.apply(lambda x: d[x.name].fit_transform(x)), ma viene generata un'eccezione: Exception: Data must be 1-dimensional. Non sono sicuro di come mi aspetto che il DataFrame risultante assomigli ... forse ogni colonna dovrebbe contenere i vettori binarizzati.
Qululu

4
Bella soluzione. Come trasformare solo in determinate colonne?
stenlytw,

1
se voglio invertire il juste di codifica per una colonna, come posso fare?
Ib D,

95

Come menzionato da Larsmans, LabelEncoder () accetta solo un array 1-d come argomento . Detto questo, è abbastanza facile ruotare il proprio codificatore di etichette che opera su più colonne di propria scelta e restituisce un frame di dati trasformato. Il mio codice qui si basa in parte sull'eccellente post sul blog di Zac Stewart trovato qui .

La creazione di un encoder personalizzato comporta semplicemente creando una classe che risponde ai fit(), transform()e fit_transform()metodi. Nel tuo caso, un buon inizio potrebbe essere qualcosa del genere:

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)

Supponiamo di voler codificare i nostri due attributi categorici ( fruite color), lasciando solo l'attributo numerico weight. Potremmo farlo come segue:

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

Che trasforma il nostro fruit_dataset di dati da

inserisci qui la descrizione dell'immagine per

inserisci qui la descrizione dell'immagine

Passandogli un frame di dati costituito interamente da variabili categoriche e omettendo il columnsparametro, ogni colonna verrà codificata (che credo sia quello che stavi cercando in origine):

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

Questo trasforma

inserisci qui la descrizione dell'immagine per

inserisci qui la descrizione dell'immagine.

Nota che probabilmente si strozzerà quando proverà a codificare attributi che sono già numerici (aggiungi del codice per gestirlo se vuoi).

Un'altra caratteristica interessante di questo è che possiamo usare questo trasformatore personalizzato in una pipeline:

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

2
Appena realizzato i dati implica che un'arancia è di colore verde. Ops. ;)
PriceHardman,

5
questo è un buon modo per trasformare i dati una volta, ma cosa succede se voglio riutilizzare questa trasformazione in un set di convalida. dovresti ripetere fit_transform e potrebbero sorgere problemi come il mio nuovo set di dati che non ha tutte le categorie per tutte le variabili. ad es., supponiamo che il colore verde non compaia nel mio nuovo set di dati. questo incasinerà la codifica.
Ben

3
Concordato con @Ben. Questo in realtà non imita sklearn oltre i nomi dei metodi. Se provassi a metterlo in una pipeline, non funzionerebbe
Tgsmith61591,

3
Per assicurarti che la codifica dell'etichetta sia coerente su tutto il treno e i set di test, ti consigliamo di eseguire la codifica sull'intero set di dati (treno + test). Questo può essere fatto prima di dividerli in treni e test, oppure è possibile combinarli, eseguire la codifica e ripartirli nuovamente.
PriceHardman,

2
Che ne dici di andare al contrario? decodifica di nuovo all'originale?
user702846

18

Da scikit-learn 0.20 puoi usare sklearn.compose.ColumnTransformere sklearn.preprocessing.OneHotEncoder:

Se hai solo variabili categoriali, OneHotEncoderdirettamente:

from sklearn.preprocessing import OneHotEncoder

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

Se hai caratteristiche tipizzate in modo eterogeneo:

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)

Altre opzioni nella documentazione: http://scikit-learn.org/stable/modules/compose.html#columntransformer-for-heterogeneous-data


inverse_transform()non è supportato su ColumnTransformer. Almeno, non per il momento: github.com/scikit-learn/scikit-learn/issues/11463 . Questo è un grosso svantaggio per la mia applicazione e probabilmente lo sarà anche per gli altri.
Sander Vanden Hautte,

16

Non abbiamo bisogno di un LabelEncoder.

È possibile convertire le colonne in categorie e quindi ottenere i loro codici. Ho usato una comprensione del dizionario qui sotto per applicare questo processo ad ogni colonna e avvolgere il risultato in un frame di dati della stessa forma con indici e nomi di colonna identici.

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

Per creare un dizionario di mappatura, puoi semplicemente enumerare le categorie usando una comprensione del dizionario:

>>> {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'}}

Se voglio tornare indietro (indietro) per una colonna (esempio variabile target: Y) come posso farlo?
Ib D,

9

questo non risponde direttamente alla tua domanda (per la quale Naputipulu Jon e PriceHardman hanno risposte fantastiche)

Tuttavia, ai fini di alcune attività di classificazione, ecc., È possibile utilizzare

pandas.get_dummies(input_df) 

questo può inserire un frame di dati con dati categorici e restituire un frame di dati con valori binari. i valori delle variabili sono codificati nei nomi delle colonne nel dataframe risultante. Di Più


6

Supponendo che tu stia semplicemente cercando di ottenere un sklearn.preprocessing.LabelEncoder()oggetto che può essere utilizzato per rappresentare le tue colonne, tutto ciò che devi fare è:

le.fit(df.columns)

Nel codice sopra avrai un numero univoco corrispondente ad ogni colonna. Più precisamente, avrai una mappatura 1: 1 di df.columnsa le.transform(df.columns.get_values()). Per ottenere la codifica di una colonna, è sufficiente passarla a le.transform(...). Ad esempio, quanto segue otterrà la codifica per ogni colonna:

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

Supponendo di voler creare un sklearn.preprocessing.LabelEncoder()oggetto per tutte le etichette delle righe, è possibile effettuare le seguenti operazioni:

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

In questo caso, molto probabilmente hai etichette di riga non uniche (come mostrato nella tua domanda). Per vedere quali classi sono state create dall'encoder le.classes_. Noterai che questo dovrebbe avere gli stessi elementi di in set(y for x in df.get_values() for y in x). Ancora una volta per convertire un'etichetta di riga in un'etichetta codificata utilizzare le.transform(...). Ad esempio, se si desidera recuperare l'etichetta per la prima colonna df.columnsdell'array e la prima riga, è possibile procedere come segue:

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

La domanda che hai avuto nel tuo commento è un po 'più complicata, ma può ancora essere realizzata:

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

Il codice sopra riportato procede come segue:

  1. Crea una combinazione unica di tutte le coppie di (colonna, riga)
  2. Rappresenta ogni coppia come una versione stringa della tupla. Questa è una soluzione alternativa per superare la LabelEncoderclasse che non supporta le tuple come nome di classe.
  3. Adatta i nuovi elementi a LabelEncoder.

Adesso usare questo nuovo modello è un po 'più complicato. Supponendo di voler estrarre la rappresentazione per lo stesso oggetto che abbiamo cercato nell'esempio precedente (la prima colonna in df.columns e la prima riga), possiamo fare questo:

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

Ricorda che ogni ricerca è ora una rappresentazione in forma di stringa di una tupla che contiene la (colonna, riga).


5

No, LabelEncodernon lo fa. Prende array 1-d di etichette di classe e produce array 1-d. È progettato per gestire le etichette di classe in problemi di classificazione, non dati arbitrari, e qualsiasi tentativo di forzarlo in altri usi richiederà il codice per trasformare il problema reale in quello che risolve (e la soluzione nello spazio originale).


Ok, dato questo, qual è il tuo suggerimento sul modo migliore in cui posso codificare le etichette di stringa di un intero DataFramealla volta?
Bryan,

@Bryan Guarda il LabelEncodercodice e adattalo . Non uso i Panda personalmente, quindi non so quanto sarà difficile.
Fred Foo,

Lascerò che anche altre pandaspersone facciano una battuta a questa domanda - sono sicuro di non essere l'unica persona con questa sfida, quindi spero che ci sia una soluzione pre-costruita là fuori.
Bryan,

5

Questo è un anno e mezzo dopo il fatto, ma anch'io, dovevo essere in grado di .transform()più colonne di frame di dati Panda contemporaneamente (e anche di poterle fare .inverse_transform()). Questo si espande sull'eccellente suggerimento di @PriceHardman sopra:

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

Esempio:

Se dfe df_copy()sono pandasframe di dati di tipo misto, è possibile applicare il MultiColumnLabelEncoder()alle dtype=objectcolonne nel modo seguente:

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

Puoi accedere a singole classi di colonne, etichette di colonne ed encoder di colonne utilizzati per adattarsi a ciascuna colonna tramite l'indicizzazione:

mcle.all_classes_
mcle.all_encoders_
mcle.all_labels_


Ciao Jason, mcle.all_labels_ non sembra funzionare (Python 3.5, Conda 4.3.29, Sklearn 0.18.1, Pandas 0.20.1. Ottengo: AttributeError: l'oggetto 'MultiColumnLabelEncoder' non ha alcun attributo 'all_labels_'
Jason

@Jason Ciao, scusa, non ho visto questo fino ad oggi: / ma se dovessi indovinare, direi che hai appena usato il fitmetodo dall'alto che in realtà non produrrà alcuna etichetta fino a quando non lo applichi ( transform/ fit_transform) a i dati.
Jason Wolosonovich,

Penso che tu debba dare un esempio migliore: non sono riuscito a rieseguire tutti i tuoi codici.
user702846

2

A seguito dei commenti sollevati sulla soluzione di @PriceHardman , proporrei la seguente versione della 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

Questa classe si adatta all'encoder sul set di addestramento e utilizza la versione montata durante la trasformazione. La versione iniziale del codice è disponibile qui .


2

Un modo breve per LabelEncoder()più colonne 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])

e puoi usarlo le_dictper labelEncode qualsiasi altra colonna:

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

2

È possibile fare tutto questo direttamente in panda ed è adatto per una capacità unica del replacemetodo.

Innanzitutto, creiamo un dizionario di dizionari che mappano le colonne e i loro valori sui loro nuovi valori di sostituzione.

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}}

Dal momento che questa sarà sempre una mappatura uno a uno, possiamo invertire il dizionario interno per ottenere una mappatura dei nuovi valori sull'originale.

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'}}

Ora, possiamo usare la capacità unica del replacemetodo di prendere un elenco nidificato di dizionari e usare le chiavi esterne come colonne e le chiavi interne come valori che vorremmo sostituire.

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

Possiamo facilmente tornare all'originale concatenando nuovamente il replacemetodo

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

2

Dopo molte ricerche e sperimentazioni con alcune risposte qui e altrove, penso che la tua risposta sia qui :

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

Ciò preserverà i nomi delle categorie tra le colonne:

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

2

Ho controllato il codice sorgente ( https://github.com/scikit-learn/scikit-learn/blob/master/sklearn/preprocessing/label.py ) di LabelEncoder. Si basava su una serie di trasformazioni intorpidite, una delle quali è np.unique (). E questa funzione accetta solo input di array 1-d. (correggimi se sbaglio).

Idee molto approssimative ... per prima cosa, identifica quali colonne necessitano di LabelEncoder, quindi esegui il ciclo attraverso ogni colonna.

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

Il df restituito sarebbe quello dopo la codifica, e label_list ti mostrerà cosa significano tutti quei valori nella colonna corrispondente. Questo è un frammento di uno script di elaborazione dati che ho scritto per lavoro. Fammi sapere se pensi che potrebbero esserci ulteriori miglioramenti.

EDIT: Voglio solo menzionare qui che i metodi sopra funzionano con frame di dati senza perdere il meglio. Non sono sicuro di come funzioni verso il frame di dati contiene dati mancanti. (Ho avuto un accordo con la procedura mancante prima di eseguire i metodi sopra)


1

se abbiamo una singola colonna per eseguire la codifica dell'etichetta e la sua inversa trasforma facilmente come farlo quando ci sono più colonne in 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

1

Se disponi di entrambi i tipi di dati numerici e categorici nel frame di dati Puoi usare: qui X è il mio frame di dati con entrambe le variabili categoriche e numeriche

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: questa tecnica è utile se non sei interessato a riconvertirli.


1

Usando Neuraxle

TLDR; È qui possibile utilizzare la FlattenForEach involucro di classe semplicemente trasformare il vostro df come: FlattenForEach(LabelEncoder(), then_unflatten=True).fit_transform(df).

Con questo metodo, il tuo codificatore di etichette sarà in grado di adattarsi e trasformarsi all'interno di una normale pipeline di scikit-learn . Importiamo semplicemente:

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

Stesso codificatore condiviso per colonne:

Ecco come verrà applicato un LabelEncoder condiviso su tutti i dati per codificarlo:

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

Risultato:

    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)

Encoder diversi per colonna:

Ed ecco come verrà applicato un primo LabelEncoder standalone sugli animali domestici e un secondo verrà condiviso per il proprietario e la posizione delle colonne. Per essere precisi, qui abbiamo un mix di codificatori di etichette diversi e condivisi:

    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)

Risultato:

    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)

0

Ha utilizzato principalmente la risposta @Alexander ma ha dovuto apportare alcune modifiche -

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

Quindi, per riutilizzarlo in futuro, puoi semplicemente salvare l'output in un documento JSON e quando ne hai bisogno lo leggi e usi la .map()funzione come ho fatto sopra.


0

Il problema è la forma dei dati (pd dataframe) che stai passando alla funzione di adattamento. Devi passare 1d elenco.


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)

Qui sto leggendo un csv da posizione e in funzione sto passando l'elenco di colonne che voglio etichettare il codice e il dataframe che voglio applicare questo.


0

Cosa ne pensi di questo?

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

Non è il più efficiente, tuttavia funziona ed è super semplice.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.