Kodowanie etykiet w wielu kolumnach w scikit-learn

216

Próbuję użyć scikit-learn LabelEncoderdo zakodowania pand DataFrameetykiet ciągów. Ponieważ ramka danych ma wiele (ponad 50) kolumn, chcę uniknąć tworzenia LabelEncoderobiektu dla każdej kolumny; Wolałbym mieć tylko jeden duży LabelEncoderobiekt, który działa we wszystkich moich kolumnach danych.

Wrzucenie całości DataFramew LabelEncoderpowoduje następujący błąd. Proszę pamiętać, że używam tutaj fałszywych danych; w rzeczywistości mam do czynienia z około 50 kolumnami danych znakowanych ciągiem, więc potrzebuję rozwiązania, które nie odwołuje się do żadnej kolumny według nazwy.

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 (ostatnie ostatnie połączenie): Plik „”, wiersz 1, w pliku ”/Users/bbalin/anaconda/lib/python2.7/site-packages/sklearn/preprocessing/label.py”, wiersz 103, w dopasowaniu y = column_or_1d (y, warn = True) Plik „/Users/bbalin/anaconda/lib/python2.7/site-packages/sklearn/utils/validation.py”, wiersz 306, w column_or_1d podnieś błąd ValueError („zły kształt wejścia { 0} ". Format (kształt)) Wartość Błąd: zły kształt wejściowy (6, 3)

Wszelkie przemyślenia na temat obejścia tego problemu?

Bryan
źródło
Dlaczego próbujesz to zrobić?
Fred Foo
Aby uprościć kodowanie wielu kolumn dataframedanych ciągu. Zbieram kodowane obiekty, więc chcę uniknąć kiszenia / usuwania 50 osobnych obiektów. Zastanawiam się również, czy istnieje sposób, aby enkoder uprościł dane, tzn. Po prostu zwraca jeden wiersz z identyfikatorem dla każdej unikalnej kombinacji zmiennych w każdej kolumnie.
Bryan
Istnieje prosty sposób, aby zrobić to wszystko w pandach, przekazując do słownika słowniki replace. Zobacz odpowiedź poniżej
Ted Petrou,

Odpowiedzi:

451

Możesz to jednak łatwo zrobić,

df.apply(LabelEncoder().fit_transform)

EDYCJA 2:

W scikit-learn 0.20 zalecanym sposobem jest

OneHotEncoder().fit_transform(df)

ponieważ OneHotEncoder obsługuje teraz wprowadzanie ciągu. Zastosowanie OneHotEncoder tylko do niektórych kolumn jest możliwe dzięki ColumnTransformer.

EDYTOWAĆ:

Ponieważ ta odpowiedź jest ponad rok temu i wygenerowała wiele pozytywnych opinii (w tym nagrodę), prawdopodobnie powinienem ją rozszerzyć.

Aby uzyskać inverse_transform i transform, musisz trochę zhakować.

from collections import defaultdict
d = defaultdict(LabelEncoder)

Dzięki temu zachowujesz teraz wszystkie kolumny LabelEncoderjako słownik.

# 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
źródło
1
To niesamowite, ale w tym przypadku jak zastosować transformację odwrotną?
Supreeth Meka,
10
Ale jeśli chcę użyć tego rozwiązania w potoku, np. Osobne dopasowanie i transformacja (dopasowanie w pociągu, a następnie użycie w zestawie testów -> ponowne użycie wyuczonego słownika) - czy jest to obsługiwane df.apply(LabelEncoder().fit_transform)?
Georg Heiler
2
Jak można to zrobić LabelBinarizerzamiast tego i ponownie użyć słownika dla zestawu testowego? Próbowałem d = defaultdict(LabelBinarizer)i wtedy fit = df.apply(lambda x: d[x.name].fit_transform(x))ale jest wyjątek: Exception: Data must be 1-dimensional. Nie jestem pewien, jak mam wyglądać wynikowy DataFrame ... może każda kolumna powinna zawierać binarne wektory.
Qululu,
4
Niezłe rozwiązanie. Jak przekształcić tylko w niektórych kolumnach?
stenlytw
1
jeśli chcę odwrócić juste kodowania dla jednej kolumny, jak to zrobić?
Ib D
95

Jak wspomniano przez larsmans, LabelEncoder () przyjmuje tylko argument 1-d jako argument . To powiedziawszy, dość łatwo jest rozwinąć własny koder etykiet, który działa na wielu wybranych kolumnach i zwraca przekształconą ramkę danych. Mój kod jest częściowo oparty na doskonałym wpisie na blogu Zac Stewarta, który można znaleźć tutaj .

Tworzenie niestandardowego koder polega po prostu tworząc klasę, która jest odpowiedzią na fit(), transform()i fit_transform()metod. W twoim przypadku dobry początek może wyglądać mniej więcej tak:

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)

Załóżmy, że chcemy zakodować nasze dwa atrybuty kategoryczne ( fruiti color), pozostawiając jednocześnie atrybut numeryczny weight. Możemy to zrobić w następujący sposób:

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

Z którego przekształca się nasz fruit_datazestaw danych

wprowadź opis zdjęcia tutaj do

wprowadź opis zdjęcia tutaj

Przekazanie jej ramki danych składającej się całkowicie ze zmiennych kategorialnych i pominięcie columnsparametru spowoduje zakodowanie każdej kolumny (która, jak sądzę, była tym, czego początkowo szukałeś):

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

To się zmienia

wprowadź opis zdjęcia tutaj do

wprowadź opis zdjęcia tutaj.

Zauważ, że prawdopodobnie będzie się dusił, gdy będzie próbował zakodować atrybuty, które są już numeryczne (jeśli chcesz, dodaj trochę kodu, aby to obsłużyć).

Kolejną miłą cechą tego jest to, że możemy użyć tego niestandardowego transformatora w potoku:

encoding_pipeline = Pipeline([
    ('encoding',MultiColumnLabelEncoder(columns=['fruit','color']))
    # add more pipeline steps as needed
])
encoding_pipeline.fit_transform(fruit_data)
PriceHardman
źródło
2
Właśnie uświadomiłem sobie, że dane sugerują, że kolor pomarańczowy jest zielony. Ups ;)
PriceHardman
5
jest to dobry sposób na jednorazową transformację danych, ale co jeśli chcę ponownie użyć tej transformacji w zestawie sprawdzania poprawności. musiałbyś ponownie dopasować_transformować i mogą pojawić się problemy, takie jak mój nowy zestaw danych, który nie ma wszystkich kategorii dla wszystkich zmiennych. np. powiedz, że kolor zielony nie pojawia się w moim nowym zestawie danych. to zepsuje kodowanie.
Ben
3
Uzgodniony z @Ben. To wcale nie naśladuje sklearn poza nazwami metod. Jeśli spróbujesz umieścić to w rurociągu, to nie zadziała
Tgsmith61591
3
Aby upewnić się, że kodowanie etykiet jest spójne zarówno dla zestawu pociągów, jak i zestawów testowych, należy wykonać kodowanie dla całego zestawu danych (pociąg + test). Można to zrobić albo przed podzieleniem ich na pociąg i przetestowanie, albo możesz je połączyć, wykonać kodowanie i ponownie je rozdzielić.
PriceHardman
2
Co powiesz na cofnięcie się? dekodowanie z powrotem do oryginału?
user702846,
18

Od scikit-learn 0.20 możesz używać sklearn.compose.ColumnTransformeri sklearn.preprocessing.OneHotEncoder:

Jeśli masz tylko zmienne kategoryczne, OneHotEncoderbezpośrednio:

from sklearn.preprocessing import OneHotEncoder

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

Jeśli masz heterogenicznie wpisane funkcje:

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)

Więcej opcji w dokumentacji: http://scikit-learn.org/stable/modules/compose.html#columntransformer-for-heterogeneous-data

ogrisel
źródło
inverse_transform()nie jest jednak obsługiwane w ColumnTransformer. Przynajmniej nie na razie: github.com/scikit-learn/scikit-learn/issues/11463 . To duża wada dla mojej aplikacji i prawdopodobnie będzie również dla innych.
Sander Vanden Hautte
16

Nie potrzebujemy LabelEncodera.

Możesz przekonwertować kolumny na kategorie, a następnie uzyskać ich kody. Użyłem poniższego słownika, aby zastosować ten proces do każdej kolumny i zawinąć wynik z powrotem w ramkę danych o tym samym kształcie z identycznymi indeksami i nazwami kolumn.

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

Aby utworzyć słownik mapowania, możesz po prostu wyliczyć kategorie, korzystając ze słownika:

>>> {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
źródło
Jeśli chcę wrócić (do tyłu) o jedną kolumnę (przykładowa zmienna docelowa: Y), jak to zrobić?
Ib D
9

to nie odpowiada bezpośrednio na twoje pytanie (na które Naputipulu Jon i PriceHardman mają fantastyczne odpowiedzi)

Jednak do celów kilku zadań klasyfikacyjnych itp. Możesz użyć

pandas.get_dummies(input_df) 

może to wprowadzić ramkę danych z danymi kategorialnymi i zwrócić ramkę danych z wartościami binarnymi. wartości zmiennych są kodowane w nazwach kolumn w wynikowej ramce danych. więcej

Anurag Priyadarshi
źródło
6

Zakładając, że po prostu próbujesz uzyskać sklearn.preprocessing.LabelEncoder()obiekt, którego można użyć do przedstawienia kolumn, wszystko, co musisz zrobić, to:

le.fit(df.columns)

W powyższym kodzie będziesz mieć unikalny numer odpowiadający każdej kolumnie. Dokładniej, będziesz miał mapowanie 1 na 1 df.columnsdla le.transform(df.columns.get_values()). Aby uzyskać kodowanie kolumny, po prostu przekaż je le.transform(...). Na przykład następujące kodowanie otrzyma kodowanie dla każdej kolumny:

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

Zakładając, że chcesz utworzyć sklearn.preprocessing.LabelEncoder()obiekt dla wszystkich etykiet wierszy, możesz wykonać następujące czynności:

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

W takim przypadku najprawdopodobniej masz nieunikalne etykiety wierszy (jak pokazano w pytaniu). Aby zobaczyć, jakie klasy utworzył koder, możesz zrobić le.classes_. Zauważysz, że powinny mieć takie same elementy jak w set(y for x in df.get_values() for y in x). Jeszcze raz przekonwertuj etykietę wiersza na użycie etykiety kodowanej le.transform(...). Na przykład, jeśli chcesz pobrać etykietę dla pierwszej kolumny w df.columnstablicy i pierwszego wiersza, możesz to zrobić:

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

Pytanie, które miałeś w swoim komentarzu, jest nieco bardziej skomplikowane, ale nadal można je zrealizować:

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

Powyższy kod wykonuje następujące czynności:

  1. Stwórz unikalną kombinację wszystkich par (kolumny, wiersza)
  2. Reprezentuj każdą parę jako łańcuchową wersję krotki. Jest to obejście umożliwiające obejście LabelEncoderklasy nieobsługującej krotek jako nazwy klasy.
  3. Pasuje nowe elementy do LabelEncoder.

Teraz korzystanie z tego nowego modelu jest nieco bardziej skomplikowane. Zakładając, że chcemy wyodrębnić reprezentację tego samego elementu, którego szukaliśmy w poprzednim przykładzie (pierwsza kolumna w df.columns i pierwszy wiersz), możemy to zrobić:

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

Pamiętaj, że każde wyszukiwanie jest teraz ciągiem reprezentującym krotkę zawierającą (kolumnę, wiersz).

TehTechGuy
źródło
5

Nie, LabelEncodernie robi tego. Pobiera tablice 1-d etykiet klas i tworzy tablice 1-d. Został zaprojektowany do obsługi etykiet klas w problemach klasyfikacyjnych, a nie arbitralnych danych, a każda próba zmuszenia go do innych zastosowań wymaga kodu, aby przekształcić rzeczywisty problem w rozwiązywany przez niego problem (i rozwiązanie z powrotem do pierwotnej przestrzeni).

Fred Foo
źródło
Ok, biorąc pod uwagę to, jaka jest twoja sugestia dotycząca najlepszego sposobu kodowania etykiet ciągów DataFramejednocześnie?
Bryan
@Bryan Spójrz na LabelEncoderkod i dostosuj go. Sam nie używam Pand, więc nie wiem, jak trudne to będzie.
Fred Foo
Pozwolę innym pandasludziom również odpowiedzieć na to pytanie - jestem pewien, że nie jestem jedyną osobą z tym wyzwaniem, więc mam nadzieję, że może istnieć gotowe rozwiązanie.
Bryan
5

Jest to półtora roku po tym fakcie, ale ja również musiałem być w stanie połączyć .transform()wiele kolumn ramek danych pand jednocześnie (i mieć możliwość .inverse_transform()ich również). Rozszerza to doskonałą sugestię @PriceHardman powyżej:

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

Przykład:

Jeśli dfi df_copy()pandasramkami danych typu mieszanego, możesz zastosować je MultiColumnLabelEncoder()do dtype=objectkolumn w następujący sposób:

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

Możesz uzyskać dostęp do poszczególnych klas kolumn, etykiet kolumn i koderów kolumn używanych do dopasowania każdej kolumny poprzez indeksowanie:

mcle.all_classes_
mcle.all_encoders_
mcle.all_labels_

Jason Wolosonovich
źródło
Cześć Jason, mcle.all_labels_ nie wydaje się działać (Python 3.5, Conda 4.3.29, Sklearn 0.18.1, Pandas 0.20.1. Otrzymuję: AttributeError: Obiekt „MultiColumnLabelEncoder” nie ma atrybutu „all_labels_”
Jason
@Jason Cześć, przepraszam, nie widziałem tego do dzisiaj: / ale gdybym musiał zgadywać, powiedziałbym, że użyłeś fitmetody z góry, która nie wytworzy żadnych etykiet, dopóki jej nie zastosujesz ( transform/ fit_transform) do dane.
Jason Wolosonovich
Myślę, że musisz dać lepszy przykład - nie mogłem ponownie uruchomić wszystkich kodów.
user702846,
2

Po komentarzach do rozwiązania @PriceHardman zaproponowałbym następującą wersję klasy:

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

Ta klasa pasuje do enkodera na zestawie treningowym i używa dopasowanej wersji podczas transformacji. Wstępną wersję kodu można znaleźć tutaj .

Dror
źródło
2

Krótka droga do LabelEncoder()wielu kolumn z 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])

i możesz użyć tego le_dictdo oznaczenia Zakoduj dowolną inną kolumnę:

le_dict[col].transform(df_another[col])
Tomek
źródło
2

Można to zrobić bezpośrednio w pandach i jest to odpowiednie dla wyjątkowej zdolności tej replacemetody.

Najpierw stwórzmy słownik słowników odwzorowujących kolumny i ich wartości na nowe wartości zastępcze.

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

Ponieważ zawsze będzie to mapowanie jeden do jednego, możemy odwrócić wewnętrzny słownik, aby uzyskać mapowanie nowych wartości z powrotem do oryginału.

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

Teraz możemy wykorzystać unikalną zdolność replacemetody do pobrania zagnieżdżonej listy słowników i użycia kluczy zewnętrznych jako kolumn, a kluczy wewnętrznych jako wartości, które chcielibyśmy zastąpić.

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

Możemy łatwo wrócić do oryginału, ponownie łącząc replacemetodę

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
źródło
2

Po wielu poszukiwaniach i eksperymentach z niektórymi odpowiedziami tutaj i gdzie indziej, myślę, że twoja odpowiedź jest tutaj :

pd.DataFrame (kolumny = df.kolumny, dane = LabelEncoder (). fit_transform (df.values.flatten ()). reshape (df.shape))

Pozwoli to zachować nazwy kategorii w kolumnach:

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
Krzysztof
źródło
2

Sprawdziłem kod źródłowy ( https://github.com/scikit-learn/scikit-learn/blob/master/sklearn/preprocessing/label.py ) LabelEncoder. Oparty był na zestawie transformacji numpy, którym jest np.unique (). Ta funkcja pobiera tylko dane z tablicy 1-d. (Popraw mnie, jeśli się mylę).

Bardzo zgrubne pomysły ... najpierw określ, które kolumny wymagają LabelEncoder, a następnie przejdź przez każdą kolumnę.

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

Zwrócony df byłby tym po kodowaniu, a etykieta_listy pokaże, co oznaczają wszystkie te wartości w odpowiedniej kolumnie. To jest fragment skryptu przetwarzania danych, który napisałem do pracy. Daj mi znać, jeśli uważasz, że można wprowadzić dalsze usprawnienia.

EDYCJA: Po prostu chcę tutaj wspomnieć, że powyższe metody działają z ramką danych, nie tracąc tego, co najlepsze. Nie wiem, jak to działa w kierunku ramki danych zawiera brakujące dane. (Miałem problem z brakującą procedurą przed wykonaniem powyższych metod)

willaccc
źródło
1

jeśli mamy jedną kolumnę do kodowania etykiety, a jej odwrotna transformacja jest łatwa, jak to zrobić, gdy w pythonie jest wiele kolumn

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
źródło
1

Jeśli masz numeryczne i kategoryczne oba typy danych w ramce danych Możesz użyć: tutaj X jest moją ramką danych posiadającą zarówno zmienne jakościowe, jak i liczbowe

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

Uwaga: Ta technika jest dobra, jeśli nie jesteś zainteresowany jej konwersją.

Vikas Gupta
źródło
1

Korzystanie z Neuraxle

TLDR; Możesz tutaj użyć klasy opakowania FlattenForEach, aby po prostu przekształcić plik df, np FlattenForEach(LabelEncoder(), then_unflatten=True).fit_transform(df). :

Dzięki tej metodzie, twój enkoder etykiet będzie mógł zmieścić się i przekształcić w zwykłym Pipeline do nauki scikit . Po prostu zaimportuj:

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

Ten sam współużytkowany koder dla kolumn:

Oto jak jeden wspólny LabelEncoder zostanie zastosowany do wszystkich danych, aby go zakodować:

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

Wynik:

    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)

Różne enkodery na kolumnę:

Oto, w jaki sposób zostanie zastosowany pierwszy samodzielny LabelEncoder na zwierzętach domowych, a drugi zostanie udostępniony właścicielowi kolumny i lokalizacji. Mówiąc ściślej, mamy tutaj mieszankę różnych i współdzielonych koderów etykiet:

    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)

Wynik:

    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
źródło
0

Używany głównie przez @Alexander, ale musiał wprowadzić pewne zmiany -

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

Następnie, aby ponownie użyć w przyszłości, możesz po prostu zapisać wynik w dokumencie json, a kiedy go potrzebujesz, wczytasz go i użyjesz .map()funkcji takiej jak ja powyżej.

bbennett36
źródło
0

Problemem jest kształt danych (ramka danych pd) przekazywanych do funkcji dopasowania. Musisz przekazać listę 1d.

Ali Sadr
źródło
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)

Tutaj czytam plik CSV z lokalizacji iw funkcji przekazuję listę kolumn, którą chcę nadać etykiecie kod i ramkę danych, którą chcę zastosować.


źródło
0

Co powiesz na to?

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

Nie jest to najbardziej wydajny, jednak działa i jest bardzo prosty.

Dominik Novotný
źródło