Кодування міток через декілька стовпців у scikit-learn


217

Я намагаюся використовувати scikit-learn's LabelEncoderдля кодування панд DataFrameрядкових міток. Оскільки у фрейму даних є багато (50+) стовпців, я хочу уникати створення LabelEncoderоб'єкта для кожного стовпця; Я вважаю за краще мати один великий LabelEncoderоб'єкт, який працює в усіх моїх стовпцях даних.

Закидання цілого DataFrameв LabelEncoderстворює помилку нижче. Будь ласка, майте на увазі, що я тут використовую фіктивні дані; насправді я маю справу з приблизно 50 стовпцями даних із мітками рядків, тому мені потрібно рішення, яке не посилається на жодні стовпці за назвою.

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 (останній дзвінок останній): Файл "", рядок 1, у файлі "/Users/bbalin/anaconda/lib/python2.7/site-packages/sklearn/preprocessing/label.py", рядок 103, відповідно до y = column_or_1d (y, warn = True) Файл "/Users/bbalin/anaconda/lib/python2.7/site-packages/sklearn/utils/validation.py", рядок 306, у столбце_or_1d підняти ValueError ("неправильна форма вводу { 0} ". Формат (форма)) ValueError: неправильна форма введення (6, 3)

Будь-які думки про те, як обійти цю проблему?


Чому ти намагаєшся це зробити?
Фред Фоо

Для спрощення кодування багатоступеневих dataframeрядкових даних. Я вибираю об'єкти (кодування), тому хочу, щоб уникнути необхідності підбирати / відбирати 50 окремих об'єктів. Крім того, мені цікаво, чи існує спосіб, щоб кодер спростити дані, тобто просто повернути один рядок з ідентифікатором для кожної унікальної комбінації змінних у кожному стовпчику.
Брайан

Існує простий спосіб зробити це все в пандах, передавши словник словників replaceметоду. Дивіться цю відповідь нижче
Тед Петру

Відповіді:


452

Ви можете легко зробити це, хоча

df.apply(LabelEncoder().fit_transform)

EDIT2:

У scikit-learn 0,20 рекомендований спосіб

OneHotEncoder().fit_transform(df)

оскільки OneHotEncoder тепер підтримує введення рядків. Застосування OneHotEncoder лише до певних стовпців можливо за допомогою ColumnTransformer.

Редагувати:

Оскільки ця відповідь була понад рік тому і призвела до багатьох результатів (включаючи щедроту), я, мабуть, повинен продовжити це.

Для inverse_transform та трансформації вам доведеться трохи зламати.

from collections import defaultdict
d = defaultdict(LabelEncoder)

З цим ви тепер зберігаєте всі стовпці LabelEncoderяк словник.

# 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
Це дивовижно, але як у цьому випадку ми можемо застосувати обернене перетворення?
Supreeth Meka

10
Але якщо я хочу використовувати це рішення в конвеєрі, наприклад, окремо підходить і трансформується (підходить у поїзд, а потім використовувати на тестовому наборі -> повторно використовувати вивчений словник) це підтримується df.apply(LabelEncoder().fit_transform)?
Георг Хайлер

2
Як з цим можна працювати, а LabelBinarizerзамість цього використовувати повторно словник для тестового набору? Я спробував d = defaultdict(LabelBinarizer)і те , fit = df.apply(lambda x: d[x.name].fit_transform(x))але виникає виняток: Exception: Data must be 1-dimensional. Я не впевнений, як я очікую виглядати в результаті DataFrame ... можливо, кожен стовпець повинен містити бінарні вектори.
Кулулу

4
Приємне рішення. Як трансформуватися лише в певний стовпець?
stenlytw

1
якщо я хочу обернути кодування juste для одного стовпця, як це зробити?
Ib D

95

Як зазначають larsmans, LabelEncoder () бере аргумент лише 1-денний масив . З цього приводу прокручувати власний кодер міток, який працює на декілька стовпців на ваш вибір, досить легко і повертає трансформований кадр даних. Мій код тут заснований частково на відмінному блозі Зака Стюарт знайшов тут .

Створення призначеного для користувача кодувальника включає в себе просто створити клас , який реагує на fit(), transform()і fit_transform()методу. У вашому випадку вдалий старт може бути приблизно таким:

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)

Припустимо, ми хочемо кодувати наші два категоричні атрибути ( fruitі color), залишаючи числовий атрибут в weightспокої. Це можна зробити так:

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

Що перетворює наш fruit_dataнабір даних

введіть тут опис зображення до

введіть тут опис зображення

Передаючи йому кадр даних, що складається повністю з категоричних змінних і опущення columnsпараметра, це призведе до кодування кожного стовпця (на який я вважаю, що ви спочатку шукали):

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

Це перетворює

введіть тут опис зображення до

введіть тут опис зображення.

Зверніть увагу, що він, ймовірно, задихнеться, коли намагатиметься кодувати атрибути, які вже є числовими (додайте код, щоб обробити це, якщо хочете).

Ще одна приємна особливість цього полягає в тому, що ми можемо використовувати цей спеціальний трансформатор в трубопроводі:

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

2
Щойно зрозумілі дані означають, що помаранчевий колір зелений. На жаль ;)
PriceHardman

5
це хороший спосіб перетворити дані один раз, але що робити, якщо я хочу повторно використовувати це перетворення на наборі перевірки. вам доведеться знову fit_transform, і можуть виникнути проблеми, такі як мій новий набір даних не містить усіх категорій для всіх змінних. Наприклад, скажімо, що зелений колір не відображається в моєму новому наборі даних. це зіпсує кодування.
Бен

3
Погоджено з @Ben. Це насправді не імітує sklearn зовсім поза іменами методу. Якщо ви спробували поставити це в трубопровід, то це не
вийшло

3
Щоб переконатися, що кодування етикетки є послідовним як для поїздів, так і для тестових наборів, вам потрібно виконати кодування для всього набору даних (поїзд + тест). Це можна зробити перед тим, як розділити їх на поїзд і випробувати, або ви зможете їх поєднати, виконати кодування та знову розділити їх.
PriceHardman

2
Як щодо переходу назад? декодування назад до оригіналу?
користувач702846

18

Оскільки scikit-learn 0,20, ви можете використовувати sklearn.compose.ColumnTransformerта sklearn.preprocessing.OneHotEncoder:

Якщо у вас є лише категоріальні змінні, OneHotEncoderбезпосередньо:

from sklearn.preprocessing import OneHotEncoder

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

Якщо у вас є різнорідні функції:

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)

Більше варіантів у документації: http://scikit-learn.org/stable/modules/compose.html#columntransformer-for-heterogeneous-data


inverse_transform()не підтримується на ColumnTransformer, хоча. Принаймні, поки що: github.com/scikit-learn/scikit-learn/isissue/11463 . Це великий мінус для мого застосування, і, мабуть, буде і для інших.
Sander Vanden Hautte

16

Нам не потрібен LabelEncoder.

Ви можете перетворити стовпці в категорії, а потім отримати їх коди. Нижче я використав розуміння словника, щоб застосувати цей процес до кожного стовпця і обернути результат назад у кадр тієї самої форми з однаковими індексами та назвами стовпців.

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

Щоб створити словник для відображення, ви можете просто перерахувати категорії за допомогою розуміння словника:

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

Якщо я хочу повернутися назад (назад) для одного стовпця (приклад цільової змінної: Y), як це зробити?
Ib D

9

це не відповідає безпосередньо на ваше запитання (на які Напутіпулу Джон та ПрайсХардман мають фантастичні відповіді)

Однак для цілей декількох завдань класифікації тощо ви можете використовувати

pandas.get_dummies(input_df) 

це може вводити кадр даних із категоричними даними та повертати кадр даних із бінарними значеннями. значення змінних кодуються в імена стовпців у отриманому кадрі даних. більше


6

Якщо припустити, що ви просто намагаєтеся отримати sklearn.preprocessing.LabelEncoder()об’єкт, який можна використовувати для представлення ваших стовпців, все, що вам потрібно зробити, це:

le.fit(df.columns)

У наведеному вище коді у вас буде унікальний номер, що відповідає кожному стовпцю. Точніше, у вас буде 1: відображення 1 з df.columnsв le.transform(df.columns.get_values()). Щоб отримати кодування стовпця, просто передайте його le.transform(...). Як приклад, наступне отримає кодування для кожного стовпця:

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

Припускаючи, що ви хочете створити sklearn.preprocessing.LabelEncoder()об’єкт для всіх міток рядків, ви можете зробити наступне:

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

У цьому випадку у вас, швидше за все, є не унікальні мітки рядків (як показано у вашому запитанні). Щоб побачити, якими класами створений кодер, який ви можете зробити le.classes_. Ви зауважите, що це має бути ті ж елементи, що і в set(y for x in df.get_values() for y in x). Ще раз перетворити мітку рядка у кодовану мітку le.transform(...). Наприклад, якщо ви хочете отримати мітку для першого стовпця в df.columnsмасиві та першого ряду, ви можете зробити це:

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

Питання, яке ви виникли у своєму коментарі, є дещо складнішим, але все ж його можна виконати:

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

Вищевказаний код робить наступне:

  1. Створіть унікальне поєднання всіх пар (стовпець, рядок)
  2. Представляйте кожну пару як рядкову версію кортежу. Це рішення для подолання LabelEncoderкласу, який не підтримує кортежі як назву класу.
  3. Підходить для нових елементів до LabelEncoder.

Тепер використовувати цю нову модель трохи складніше. Припускаючи, що ми хочемо витягнути представлення для того ж елемента, який ми шукали у попередньому прикладі (перший стовпець у df.column та перший рядок), ми можемо це зробити:

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

Пам'ятайте, що кожен пошук тепер є рядковим зображенням кортежу, який містить (стовпець, рядок).


5

Ні, LabelEncoderне робить цього. Він займає 1-d масиви міток класу і виробляє 1-d масиви. Він призначений для обробки міток класу в проблемах класифікації, а не довільних даних, і будь-яка спроба примусити їх до іншого використання потребує коду для перетворення фактичної проблеми в проблему, яку вона вирішує (і рішення назад у вихідний простір).


Гаразд, враховуючи це, яка ваша пропозиція щодо найкращого способу кодування рядкових міток цілим DataFrameза один раз?
Брайан

@Bryan Подивіться на LabelEncoderкод і адаптуйте його. Я сам не користуюся Pandas, тому не знаю, наскільки це буде важко.
Фред Фо

Я дозволю й іншим pandasлюдям розірвати це питання - я впевнений, що я не єдина людина, яка має це завдання, тому сподіваюся, що там може бути заздалегідь розроблене рішення.
Брайан

5

Це рік-півтора після факту, але мені теж потрібно було мати можливість .transform()одночасно робити декілька стовпців фреймів даних (і бути в змозі .inverse_transform()також). Це пояснюється чудовою пропозицією @PriceHardman вище:

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

Приклад:

Якщо dfі df_copy()є змішані pandasкадри даних, ви можете застосувати MultiColumnLabelEncoder()до dtype=objectстовпців наступним чином:

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

Ви можете отримати доступ до окремих класів стовпців, міток стовпців та кодерів стовпців, які використовуються для підгонки кожного стовпця шляхом індексації:

mcle.all_classes_
mcle.all_encoders_
mcle.all_labels_


Вітаю, Jason, mcle.all_labels_ не працює (Python 3.5, Conda 4.3.29, Sklearn 0.18.1, Pandas 0.20.1. Я отримую: AttributeError: 'MultiColumnLabelEncoder' об’єкт не має атрибута 'all_labels_'
Jason

@Jason Привіт, вибачте, я не бачив цього до сьогодні: / але якщо мені довелося здогадатися, я б сказав, що ви просто використовували fitметод зверху, який фактично не створює міток, поки ви не застосуєте його ( transform/ fit_transform) до дані.
Джейсон Волосонович

Я думаю, вам потрібно навести кращий приклад - я не зміг повторити всі ваші коди.
користувач702846

2

Слідом за коментарями, висловленими щодо рішення @PriceHardman, я запропонував би наступну версію класу:

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

Цей клас поміщає кодер на навчальний набір і використовує пристосовану версію при трансформації. Початкову версію коду можна знайти тут .


2

Короткий шлях до LabelEncoder()кількох стовпців 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])

і ви можете використовувати це le_dictдля позначенняEncode будь-якого іншого стовпця:

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

2

Це можливо зробити все в пандах безпосередньо і добре підходить для унікальної здатності replaceметоду.

Спочатку давайте складемо словник словників, які відображають стовпці та їх значення до їх нових замінних значень.

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

Оскільки це буде завжди відображенням один на один, ми можемо перевернути внутрішній словник, щоб повернути відображення нових значень до початкових.

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

Тепер ми можемо використовувати унікальну здатність replaceметоду приймати вкладений список словників і використовувати зовнішні клавіші як стовпці, а внутрішні клавіші як значення, які ми хотіли б замінити.

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

Ми можемо легко повернутися до оригіналу, знову застосувавши replaceметод

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

2

Після багатьох пошуків та експериментів з деякими відповідями тут і деінде, я думаю, що ваша відповідь тут :

pd.DataFrame (стовпці = df.колонки, дані = LabelEncoder (). fit_transform (df.values.flatten ()). переформатувати (df.shape))

Це збереже назви категорій у стовпцях:

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

Я перевірив вихідний код ( https://github.com/scikit-learn/scikit-learn/blob/master/sklearn/preprocessing/label.py ) програми LabelEncoder. Він ґрунтувався на наборі перетворення нумерів, одним із яких є np.unique (). І ця функція займає лише 1-d вхід масиву. (виправте мене, якщо я помиляюся).

Дуже грубі ідеї ... спочатку визначте, для яких стовпців потрібен LabelEncoder, потім проведіть цикл через кожен стовпець.

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

Після кодування буде повернутий df , а label_list покаже, що всі ці значення означають у відповідному стовпці. Це фрагмент із сценарію обробки даних, який я написав для роботи. Повідомте мене, якщо ви думаєте, що можливе подальше вдосконалення.

EDIT: Просто хочу зазначити, що вищезазначені методи працюють з фреймом даних, не пропускаючи кращих. Не впевнений, як він працює в рамках кадру даних, що містить відсутні дані. (Я мав справу з пропущеною процедурою, перш ніж виконувати вищевказані методи)


1

якщо у нас є один стовпець, щоб зробити кодування мітки та її зворотне перетворення, це легко, як це зробити, коли в 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

Якщо у вас є числовий і категоричний обидва типи даних у фреймі даних, ви можете використовувати: тут X - мій кадр даних, що має обидві змінні категорії і числові.

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

Примітка. Ця методика хороша, якщо ви не зацікавлені в їх перетворенні.


1

Використання Neuraxle

TLDR; Ви тут можете використовувати FlattenForEach обгортки класу просто перетворити ваш ФР , як: FlattenForEach(LabelEncoder(), then_unflatten=True).fit_transform(df).

За допомогою цього методу ваш кодер міток зможе поміститися та перетворитись у звичайний конвеєр Scikit . Давайте просто імпортуємо:

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

Той самий спільний кодер для стовпців:

Ось як один спільний LabelEncoder буде застосовано до всіх даних для його кодування:

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

Результат:

    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)

Різні кодери на стовпчик:

Ось як перший автономний LabelEncoder буде застосований до домашніх тварин, а другий буде розділений для власника та місцезнаходження стовпців. Точніше, ми маємо поєднання різних кодерів етикетки та спільного використання:

    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)

Результат:

    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

В основному використовується відповідь @Alexander, але довелося внести деякі зміни -

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

Тоді для подальшого повторного використання ви можете просто зберегти вихід у документ json, і коли вам це потрібно, ви прочитаєте його та використаєте .map()функцію, як я це робив вище.


0

Проблема полягає у формі даних (pd dataframe), які ви переходите до функції fit. Ви повинні пройти 1d список.


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)

Тут я читаю CSV з розташування та за функцією, я передаю список стовпців, я хочу зазначити код мітки та фрейм даних, який я хочу застосувати.


0

Як щодо цього?

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

Він не найефективніший, проте він працює і він дуже простий.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.