У меня проблема классификации машинного обучения с 80% категориальных переменных. Должен ли я использовать одно горячее кодирование, если я хочу использовать какой-то классификатор для классификации? Могу ли я передать данные классификатору без кодировки?

Я пытаюсь сделать следующее для выбора функции:

  1. Читал файл поезда:

    num_rows_to_read = 10000
    train_small = pd.read_csv ("../../ dataset / train.csv", nrows = num_rows_to_read)
    
  2. Я меняю тип категориальных признаков на «категория»:

    non_categorial_features = ['orig_destination_distance',
                              'srch_adults_cnt',
                              'srch_children_cnt',
                              'srch_rm_cnt',
                              'cnt']
    
    для category_feature в списке (train_small.columns):
        если category_feature не входит в non_categorial_features:
            train_small [категориальная_ функция] = train_small [категориальная_ функция] .astype ('категория')
    
  3. Я использую одно горячее кодирование:

    train_small_with_dummies = pd.get_dummies (train_small, sparse = True)
    

Проблема в том, что 3-я часть часто застревает, хотя я использую более крепкую машину.

Таким образом, без единого горячего кодирования я не могу выбрать какую-либо функцию для определения важности функций.

Что посоветуете?

Ответы (21)

Во-первых, самый простой способ одного горячего кодирования: использовать Sklearn.

http://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.OneHotEncoder.html

Во-вторых, я не думаю, что использование pandas для одного горячего кодирования - это так просто (хотя и не подтверждено)

Создание фиктивных переменных в пандах для Python

И напоследок, нужно ли вам одно горячее кодирование? Одно горячее кодирование экспоненциально увеличивает количество функций, резко увеличивая время работы любого классификатора или всего, что вы собираетесь запускать. Особенно, когда у каждой категориальной характеристики много уровней. Вместо этого вы можете использовать фиктивное кодирование.

Использование фиктивного кодирования обычно работает хорошо, значительно сокращая время выполнения и сложность. Один мудрый профессор однажды сказал мне: «Лучше меньше, да лучше».

Вот код моей пользовательской функции кодирования, если хотите.

from sklearn.preprocessing import LabelEncoder

#Auto encodes any dataframe column of type category or object.
def dummyEncode(df):
        columnsToEncode = list(df.select_dtypes(include=['category','object']))
        le = LabelEncoder()
        for feature in columnsToEncode:
            try:
                df[feature] = le.fit_transform(df[feature])
            except:
                print('Error encoding '+feature)
        return df

РЕДАКТИРОВАТЬ: Сравнение для большей ясности:

Быстрое кодирование: преобразование n уровней в n-1 столбец.

Index  Animal         Index  cat  mouse
  1     dog             1     0     0
  2     cat       -->   2     1     0
  3    mouse            3     0     1

Вы можете увидеть, как это взорвет вашу память, если у вас есть много разных типов (или уровней) в вашей категориальной функции. Имейте в виду, это всего лишь ОДИН столбец.

Пустое кодирование:

Index  Animal         Index  Animal
  1     dog             1      0   
  2     cat       -->   2      1 
  3    mouse            3      2

Вместо этого преобразовать в числовые представления. Значительно экономит место для функций за счет небольшой точности.

Подход 1: вы можете использовать pandas 'pd.get_dummies.

Пример 1:

import pandas as pd
s = pd.Series(list('abca'))
pd.get_dummies(s)
Out[]: 
     a    b    c
0  1.0  0.0  0.0
1  0.0  1.0  0.0
2  0.0  0.0  1.0
3  1.0  0.0  0.0

Пример 2:

Следующее преобразует данный столбец в один горячий. Используйте префикс, чтобы иметь несколько манекенов.

import pandas as pd
        
df = pd.DataFrame({
          'A':['a','b','a'],
          'B':['b','a','c']
        })
df
Out[]: 
   A  B
0  a  b
1  b  a
2  a  c

# Get one hot encoding of columns B
one_hot = pd.get_dummies(df['B'])
# Drop column B as it is now encoded
df = df.drop('B',axis = 1)
# Join the encoded df
df = df.join(one_hot)
df  
Out[]: 
       A  a  b  c
    0  a  0  1  0
    1  b  1  0  0
    2  a  0  0  1

Подход 2: Используйте Scikit-learn

Использование OneHotEncoder имеет то преимущество, что можно подогнать к некоторым обучающим данным, а затем преобразовать к некоторым другим данным, используя тот же экземпляр. У нас также есть handle_unknown для дальнейшего управления действиями кодировщика с unseen данными.

Учитывая набор данных с тремя функциями и четырьмя выборками, мы позволяем кодировщику находить максимальное значение для каждой функции и преобразовывать данные в двоичное кодирование с возможностью горячей замены.

>>> from sklearn.preprocessing import OneHotEncoder
>>> enc = OneHotEncoder()
>>> enc.fit([[0, 0, 3], [1, 1, 0], [0, 2, 1], [1, 0, 2]])   
OneHotEncoder(categorical_features='all', dtype=,
   handle_unknown='error', n_values='auto', sparse=True)
>>> enc.n_values_
array([2, 3, 4])
>>> enc.feature_indices_
array([0, 2, 5, 9], dtype=int32)
>>> enc.transform([[0, 1, 1]]).toarray()
array([[ 1.,  0.,  0.,  1.,  0.,  0.,  1.,  0.,  0.]])

Вот ссылка на этот пример: http://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.OneHotEncoder.html

Вы можете сделать это с помощью numpy.eye и a, используя механизм выбора элемента массива:

import numpy as np
nb_classes = 6
data = [[2, 3, 4, 0]]

def indices_to_one_hot(data, nb_classes):
    """Convert an iterable of indices to one-hot encoded labels."""
    targets = np.array(data).reshape(-1)
    return np.eye(nb_classes)[targets]

Возвращаемое значение indices_to_one_hot (nb_classes, data) теперь

array([[[ 0.,  0.,  1.,  0.,  0.,  0.],
        [ 0.,  0.,  0.,  1.,  0.,  0.],
        [ 0.,  0.,  0.,  0.,  1.,  0.],
        [ 1.,  0.,  0.,  0.,  0.,  0.]]])

.reshape (-1) нужен, чтобы убедиться, что у вас правильный формат меток (вы также можете иметь [[2], [3], [4], [0] ]]).

Одноразовое кодирование требует немного большего, чем преобразование значений в индикаторные переменные. Обычно процесс машинного обучения требует, чтобы вы применили это кодирование несколько раз для проверки или тестирования наборов данных и применили модель, которую вы построили, к наблюдаемым данным в реальном времени. Вы должны сохранить отображение (преобразование), которое использовалось для построения модели. Хорошее решение - использовать DictVectorizer или LabelEncoder (за которым следует get_dummies. Вот функция, которую вы можете использовать:

def oneHotEncode2(df, le_dict = {}):
    if not le_dict:
        columnsToEncode = list(df.select_dtypes(include=['category','object']))
        train = True;
    else:
        columnsToEncode = le_dict.keys()   
        train = False;

    for feature in columnsToEncode:
        if train:
            le_dict[feature] = LabelEncoder()
        try:
            if train:
                df[feature] = le_dict[feature].fit_transform(df[feature])
            else:
                df[feature] = le_dict[feature].transform(df[feature])

            df = pd.concat([df, 
                              pd.get_dummies(df[feature]).rename(columns=lambda x: feature + '_' + str(x))], axis=1)
            df = df.drop(feature, axis=1)
        except:
            print('Error encoding '+feature)
            #df[feature]  = df[feature].convert_objects(convert_numeric='force')
            df[feature]  = df[feature].apply(pd.to_numeric, errors='coerce')
    return (df, le_dict)

Это работает с фреймом данных pandas, и для каждого столбца фрейма данных он создает и возвращает отображение обратно. Вы бы назвали это так:

train_data, le_dict = oneHotEncode2(train_data)

Затем на тестовых данных вызов выполняется путем передачи словаря, возвращенного из обучения:

test_data, _ = oneHotEncode2(test_data, le_dict)

Эквивалентным методом является использование DictVectorizer. Соответствующий пост об этом есть в моем блоге. Я упоминаю об этом здесь, поскольку он дает некоторые аргументы в пользу этого подхода вместо простого использования get_dummies post (раскрытие: это мой собственный блог).

Here i tried with this approach :

import numpy as np
#converting to one_hot





def one_hot_encoder(value, datal):

    datal[value] = 1

    return datal


def _one_hot_values(labels_data):
    encoded = [0] * len(labels_data)

    for j, i in enumerate(labels_data):
        max_value = [0] * (np.max(labels_data) + 1)

        encoded[j] = one_hot_encoder(i, max_value)

    return np.array(encoded)

Вы можете передавать данные в классификатор catboost без кодирования. Catboost сам обрабатывает категориальные переменные, выполняя одноразовое и целевое кодирование с расширяющимся средним.

Короткий ответ

Вот функция для быстрого кодирования без с использованием numpy, pandas или других пакетов. Требуется список целых чисел, логических значений или строк (а также, возможно, других типов).

импорт набора текста


def one_hot_encode (items: list) -> typing.List [список]:
    результаты = []
    # найти уникальные элементы (мы хотим, чтобы уникальные элементы, b / c повторяющиеся элементы будут иметь одинаковую кодировку)
    unique_items = список (набор (элементы))
    # сортируем уникальные предметы
    sorted_items = отсортировано (уникальные_элементы)
    # найти, какой длины должен быть список каждого элемента
    max_index = len (уникальные_элементы)

    для элемента в элементах:
        # создаем список нулей соответствующей длины
        one_hot_encoded_result = [0 для i в диапазоне (0, max_index)]
        # найти индекс элемента
        one_hot_index = sorted_items.index (элемент)
        # меняем ноль в индексе предыдущей строки на единицу
        one_hot_encoded_result [one_hot_index] = 1
        # добавляем результат
        results.append (one_hot_encoded_result)

    вернуть результаты

Пример:

one_hot_encode ([2, 1, 1, 2, 5, 3])

# [[0, 1, 0, 0],
# [1, 0, 0, 0],
# [1, 0, 0, 0],
# [0, 1, 0, 0],
# [0, 0, 0, 1],
# [0, 0, 1, 0]]
one_hot_encode ([True, False, True])

# [[0, 1], [1, 0], [0, 1]]
one_hot_encode (['a', 'b', 'c', 'a', 'e'])

# [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [1, 0, 0, 0], [0, 0, 0, 1 ]]

Длинный (эр) ответ

Я знаю, что на этот вопрос уже есть много ответов, но заметил две вещи. Во-первых, в большинстве ответов используются такие пакеты, как numpy и / или pandas. И это хорошо. Если вы пишете производственный код, вам, вероятно, следует использовать надежные и быстрые алгоритмы, подобные тем, которые представлены в пакетах numpy / pandas. Но я считаю, что в образовательных целях кто-то должен дать ответ с прозрачным алгоритмом, а не просто реализацией чужого алгоритма. Во-вторых, я заметил, что многие ответы не обеспечивают надежной реализации однократного кодирования, потому что они не соответствуют одному из требований ниже. Ниже приведены некоторые из требований (как я их вижу) для полезной, точной и надежной функции однократного кодирования:

Функция однократного кодирования должна:

  • обрабатывать список различных типов (например, целые числа, строки, числа с плавающей запятой и т. Д.) Как входные
  • обрабатывать список ввода с дубликатами
  • возвращает список списков, соответствующих (в том же порядке, что и) входам
  • возвращает список списков, каждый из которых является как можно короче

Я проверил многие ответы на этот вопрос, и большинство из них не соответствуют одному из вышеперечисленных требований.

Одно горячее кодирование с помощью pandas очень просто:

def one_hot(df, cols):
    """
    @param df pandas DataFrame
    @param cols a list of columns to encode 
    @return a DataFrame with one-hot encoding
    """
    for each in cols:
        dummies = pd.get_dummies(df[each], prefix=each, drop_first=False)
        df = pd.concat([df, dummies], axis=1)
    return df

РЕДАКТИРОВАТЬ:

Другой способ one_hot с использованием sklearn LabelBinarizer:

from sklearn.preprocessing import LabelBinarizer 
label_binarizer = LabelBinarizer()
label_binarizer.fit(all_your_labels_list) # need to be global or remembered to use it later

def one_hot_encode(x):
    """
    One hot encode a list of sample labels. Return a one-hot encoded vector for each label.
    : x: List of sample Labels
    : return: Numpy array of one-hot encoded labels
    """
    return label_binarizer.transform(x)

Я знаю, что опаздываю на эту вечеринку, но самый простой способ горячего кодирования фрейма данных автоматическим способом - использовать эту функцию:

def hot_encode(df):
    obj_df = df.select_dtypes(include=['object'])
    return pd.get_dummies(df, columns=obj_df.columns).values

Расширяющий ответ @Martin Thoma

def one_hot_encode(y):
    """Convert an iterable of indices to one-hot encoded labels."""
    y = y.flatten() # Sometimes not flattened vector is passed e.g (118,1) in these cases
    # the function ends up creating a tensor e.g. (118, 2, 1). flatten removes this issue
    nb_classes = len(np.unique(y)) # get the number of unique classes
    standardised_labels = dict(zip(np.unique(y), np.arange(nb_classes))) # get the class labels as a dictionary
    # which then is standardised. E.g imagine class labels are (4,7,9) if a vector of y containing 4,7 and 9 is
    # directly passed then np.eye(nb_classes)[4] or 7,9 throws an out of index error.
    # standardised labels fixes this issue by returning a dictionary;
    # standardised_labels = {4:0, 7:1, 9:2}. The values of the dictionary are mapped to keys in y array.
    # standardised_labels also removes the error that is raised if the labels are floats. E.g. 1.0; element
    # cannot be called by an integer index e.g y[1.0] - throws an index error.
    targets = np.vectorize(standardised_labels.get)(y) # map the dictionary values to array.
    return np.eye(nb_classes)[targets]

Я использовал это в своей акустической модели: вероятно, это помогает в вашей модели.

def one_hot_encoding(x, n_out):
    x = x.astype(int)  
    shape = x.shape
    x = x.flatten()
    N = len(x)
    x_categ = np.zeros((N,n_out))
    x_categ[np.arange(N), x] = 1
    return x_categ.reshape((shape)+(n_out,))

Вот решение, использующее DictVectorizer и метод Pandas DataFrame.to_dict ('records').

>>> import pandas as pd
>>> X = pd.DataFrame({'income': [100000,110000,90000,30000,14000,50000],
                      'country':['US', 'CAN', 'US', 'CAN', 'MEX', 'US'],
                      'race':['White', 'Black', 'Latino', 'White', 'White', 'Black']
                     })

>>> from sklearn.feature_extraction import DictVectorizer
>>> v = DictVectorizer()
>>> qualitative_features = ['country','race']
>>> X_qual = v.fit_transform(X[qualitative_features].to_dict('records'))
>>> v.vocabulary_
{'country=CAN': 0,
 'country=MEX': 1,
 'country=US': 2,
 'race=Black': 3,
 'race=Latino': 4,
 'race=White': 5}

>>> X_qual.toarray()
array([[ 0.,  0.,  1.,  0.,  0.,  1.],
       [ 1.,  0.,  0.,  1.,  0.,  0.],
       [ 0.,  0.,  1.,  0.,  1.,  0.],
       [ 1.,  0.,  0.,  0.,  0.,  1.],
       [ 0.,  1.,  0.,  0.,  0.,  1.],
       [ 0.,  0.,  1.,  1.,  0.,  0.]])

Предположим, что из 10 переменных у вас есть 3 категориальные переменные во фрейме данных с именами cname1, cname2 и cname3. Затем следующий код автоматически создаст одну переменную с горячим кодированием в новом фрейме данных.

import category_encoders as ce
encoder_var=ce.OneHotEncoder(cols=['cname1','cname2','cname3'],handle_unknown='return_nan',return_df=True,use_cat_names=True)
new_df = encoder_var.fit_transform(old_df)

pandas as имеет встроенную функцию "get_dummies" для получения одной горячей кодировки этого конкретного столбца / с.

однострочный код для быстрого кодирования:

df=pd.concat([df,pd.get_dummies(df['column name'],prefix='column name')],axis=1).drop(['column name'],axis=1)

Вы также можете сделать следующее. Обратите внимание, что ниже вам не обязательно использовать pd.concat.

import pandas as pd 
# intialise data of lists. 
data = {'Color':['Red', 'Yellow', 'Red', 'Yellow'], 'Length':[20.1, 21.1, 19.1, 18.1],
       'Group':[1,2,1,2]} 

# Create DataFrame 
df = pd.DataFrame(data) 

for _c in df.select_dtypes(include=['object']).columns:
    print(_c)
    df[_c]  = pd.Categorical(df[_c])
df_transformed = pd.get_dummies(df)
df_transformed

Вы также можете изменить явные столбцы на категориальные. Например, здесь я меняю Color и Group

import pandas as pd 
# intialise data of lists. 
data = {'Color':['Red', 'Yellow', 'Red', 'Yellow'], 'Length':[20.1, 21.1, 19.1, 18.1],
       'Group':[1,2,1,2]} 

# Create DataFrame 
df = pd.DataFrame(data) 
columns_to_change = list(df.select_dtypes(include=['object']).columns)
columns_to_change.append('Group')
for _c in columns_to_change:
    print(_c)
    df[_c]  = pd.Categorical(df[_c])
df_transformed = pd.get_dummies(df)
df_transformed

Попробуйте это:

!pip install category_encoders
import category_encoders as ce

categorical_columns = [...the list of names of the columns you want to one-hot-encode ...]
encoder = ce.OneHotEncoder(cols=categorical_columns, use_cat_names=True)
df_train_encoded = encoder.fit_transform(df_train_small)

df_encoded.head ()

Результирующий фрейм данных df_train_encoded такой же, как и исходный, но категориальные функции теперь заменены их версиями с горячим кодированием.

Подробнее о category_encoders здесь.

У меня работает:

pandas.factorize( ['B', 'C', 'D', 'B'] )[0]

Вывод:

[0, 1, 2, 0]

Чтобы добавить к другим вопросам, позвольте мне рассказать, как я сделал это с помощью функции Python 2.0 с помощью Numpy:

def one_hot(y_):
    # Function to encode output labels from number indexes 
    # e.g.: [[5], [0], [3]] --> [[0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0]]

    y_ = y_.reshape(len(y_))
    n_values = np.max(y_) + 1
    return np.eye(n_values)[np.array(y_, dtype=np.int32)]  # Returns FLOATS

Строку n_values ​​= np.max (y_) + 1 можно жестко запрограммировать, чтобы вы использовали достаточное количество нейронов, например, в случае использования мини-пакетов.

Демо-проект / учебник, в котором использовалась эта функция: https://github.com/guillaume-chevalier/LSTM-Human-Activity-Recognition

Это может и должно быть просто:

class OneHotEncoder:
    def __init__(self,optionKeys):
        length=len(optionKeys)
        self.__dict__={optionKeys[j]:[0 if i!=j else 1 for i in range(length)] for j in range(length)}

Использование:

ohe=OneHotEncoder(["A","B","C","D"])
print(ohe.A)
print(ohe.D)

Вы можете использовать функцию numpy.eye.

import numpy as np

def one_hot_encode(x, n_classes):
    """
    One hot encode a list of sample labels. Return a one-hot encoded vector for each label.
    : x: List of sample Labels
    : return: Numpy array of one-hot encoded labels
     """
    return np.eye(n_classes)[x]

def main():
    list = [0,1,2,3,4,3,2,1,0]
    n_classes = 5
    one_hot_list = one_hot_encode(list, n_classes)
    print(one_hot_list)

if __name__ == "__main__":
    main()

Результат

D:\Desktop>python test.py
[[ 1.  0.  0.  0.  0.]
 [ 0.  1.  0.  0.  0.]
 [ 0.  0.  1.  0.  0.]
 [ 0.  0.  0.  1.  0.]
 [ 0.  0.  0.  0.  1.]
 [ 0.  0.  0.  1.  0.]
 [ 0.  0.  1.  0.  0.]
 [ 0.  1.  0.  0.  0.]
 [ 1.  0.  0.  0.  0.]]

Намного проще использовать Pandas для простого однократного кодирования. Если вам нужны дополнительные параметры, вы можете использовать scikit-learn.

Для базового однократного кодирования с помощью Pandas вы передаете свой фрейм данных в функцию get_dummies.

Например, если у меня есть фрейм данных с именем imdb_movies:

enter image description here

... и я хочу сразу закодировать столбец с рейтингом, я делаю это:

pd.get_dummies(imdb_movies.Rated)

enter image description here

Это возвращает новый фрейм данных со столбцом для каждого существующего рейтинга «level» вместе с 1 или 0, указывающими наличие этого рейтинга для данного наблюдения. .

Обычно мы хотим, чтобы это было частью исходного фрейма данных. В этом случае мы присоединяем наш новый фиктивный кодированный фрейм к исходному фрейму, используя "привязку столбца.

Мы можем привязать столбцы с помощью Pandas concat function:

rated_dummies = pd.get_dummies(imdb_movies.Rated)
pd.concat([imdb_movies, rated_dummies], axis=1)

enter image description here

Теперь мы можем запустить анализ нашего полного фрейма данных.

ПРОСТАЯ УТИЛИТНАЯ ФУНКЦИЯ

Я бы порекомендовал себе служебную функцию, чтобы сделать это быстро:

def encode_and_bind(original_dataframe, feature_to_encode):
    dummies = pd.get_dummies(original_dataframe[[feature_to_encode]])
    res = pd.concat([original_dataframe, dummies], axis=1)
    return(res)

Использование:

encode_and_bind(imdb_movies, 'Rated')

Результат:

enter image description here

Кроме того, согласно комментарию @pmalbu, если вы хотите, чтобы функция удалила исходный код feature_to_encode, используйте эту версию:

def encode_and_bind(original_dataframe, feature_to_encode):
    dummies = pd.get_dummies(original_dataframe[[feature_to_encode]])
    res = pd.concat([original_dataframe, dummies], axis=1)
    res = res.drop([feature_to_encode], axis=1)
    return(res) 

Вы можете кодировать несколько функций одновременно следующим образом:

features_to_encode = ['feature_1', 'feature_2', 'feature_3',
                      'feature_4']
for feature in features_to_encode:
    res = encode_and_bind(train_set, feature)

2022 WebDevInsider