Руководство по кодированию категориальных значений в Python

Open in Colab


telegram

Введение

Часто наборы данных содержат категориальные переменные.

дополнительно см. статью Использование типа данных категории в pandas.

Эти переменные обычно хранятся в виде текстовых значений, которые представляют различные характеристики. Некоторые примеры включают цвет ("Красный", "Желтый", "Синий"), размер ("Маленький", "Средний", "Большой") или географические обозначения ("Штат" или "Страна").

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

Оригинал статьи Криса по ссылке

Как и во многих других аспектах здесь нет однозначного ответа. Каждый подход имеет свои плюсы/минусы и может повлиять на результат анализа. К счастью, инструменты Python, такие как pandas и scikit-learn, предоставляют несколько методик. Эта статья является обзором популярных (и более сложных) подходов в надежде, что это поможет другим применить рассмотренные методы к своим задачам.

Набор данных

Для этой статьи мне удалось найти хороший набор данных в репозитории машинного обучения UCI. Этот автомобильный набор данных включает хорошее сочетание категориальных, а также непрерывных значений и служит полезным примером. Поскольку понимание предметной области является важным аспектом при принятии решения о том, как кодировать различные категориальные значения, этот набор данных является хорошим примером.

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

К счастью, в pandas это делается просто:

Посмотрим, какие типы данных у нас есть:

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

дополнительно см. статью Обзор типов данных Pandas)

В pandas есть полезная функция select_dtypes, которую можно использовать для создания нового фрейма данных, содержащего только столбцы типа object:

Прежде чем идти дальше, в данных есть пара нулевых значений, которые необходимо очистить:

Для простоты заполните значение числом four (так как это наиболее распространенное значение):

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

Подход №1 - Найти и заменить

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

Есть два столбца данных, значения которых представляют собой слова, используемые для представления чисел. В частности, количество цилиндров в двигателе (num_cylinders) и количество дверей в машине (num_doors). Pandas позволяет нам напрямую заменять текстовые значения их числовыми эквивалентами, используя replace.

Мы уже видели, что данные num_doors включают только 2 или 4 двери. Количество цилиндров включает всего 7 значений, которые легко переводятся в действительные числа:

Если вы просмотрите документацию по replace, то увидите, что это мощная функция с множеством параметров.

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

Вот полный словарь для очистки столбцов num_doors и num_cylinders:

Чтобы преобразовать столбцы в числа с помощью replace:

Хорошим преимуществом этого подхода является то, что pandas "знает" типы значений в столбцах, поэтому теперь объект имеет тип int64:

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

Подход № 2 - Кодирование метки

Другой подход к кодированию категориальных значений заключается в использовании метода, называемого кодированием меток (label encoding).

Кодирование метки - это простое преобразование каждого значения в столбце в число. Например, столбец body_style содержит 5 разных значений. Мы могли бы закодировать это так:

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

Затем вы можете назначить закодированную переменную новому столбцу с помощью метода доступа (accessor) cat.codes:

Приятным аспектом этого подхода является то, что вы получаете преимущества категорий pandas (компактный размер данных, возможность упорядочивания, поддержка построения графиков), но их можно легко преобразовать в числовые значения для дальнейшего анализа.

Подход № 3 - Унитарное кодирование (One Hot Encoding)

Кодирование меток имеет тот недостаток, что числовые значения могут быть "неверно интерпретированы" алгоритмами. Например, значение 0 очевидно меньше значения 4, но действительно ли это соответствует набору данных в реальной жизни? Имеет ли универсал в 4 раза больший вес, чем у кабриолета? В этом примере я так не думаю.

Общий альтернативный подход называется унитарным кодированием (One Hot Encoding). Основная стратегия состоит в том, чтобы преобразовать значение каждой категории в новый столбец и присвоить столбцу значение 1 или 0 (Истина / Ложь). Это дает преимущество в том, что значение не взвешивается неправильно, но имеет обратную сторону добавления дополнительных столбцов в набор данных.

Pandas поддерживает эту возможность с помощью get_dummies. Эта функция названа так, потому что она создает фиктивные (dummy) / индикаторные переменные (1 или 0).

Надеюсь, простой пример прояснит это. Мы можем посмотреть на столбец drive_wheels, где у нас есть значения 4wd, fwd или rwd.

Используя get_dummies, мы можем преобразовать их в три столбца с 1 или 0, соответствующими правильному значению:

Новый набор данных содержит три новых столбца:

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

Правильное присвоение имен немного упростит дальнейший анализ:

Другая концепция, о которой следует помнить, заключается в том, что get_dummies возвращает полный фрейм данных (dataframe), поэтому вам нужно будет отфильтровать объекты с помощью select_dtypes перед проведением итогового анализа.

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

Подход №4 - Пользовательское двоичное кодирование

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

В этом конкретном наборе данных есть столбец с именем engine_type, который содержит несколько разных значений:

Возможно, нас волнует, оснащен ли двигатель верхним распредвалом (Overhead Cam, OHC) или нет.

Другими словами, разные версии OHC одинаковы для этого анализа. Если это так, то мы могли бы использовать метод доступа (accessor) str и np.where (функциональная замена условия) для создания нового столбца, который указывает, есть ли в автомобиле двигатель OHC:

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

Результирующий фрейм данных выглядит следующим образом (показывает только подмножество столбцов):

Это подход подчеркивает, насколько важно знание предметной области для эффективного решения проблемы.

scikit-Learn

В дополнение к подходу pandas, scikit-learn предоставляет аналогичную функциональность. Лично я считаю, что использование pandas проще для понимания, но подход scikit является оптимальным, когда вы пытаетесь построить прогнозную модель.

Например, если мы хотим выполнить кодирование меток для марки автомобиля (make), нам нужно создать экземпляр объекта OrdinalEncoder и произвести fit_transform данных:

scikit-learn также поддерживает двоичное кодирование с помощью OneHotEncoder.

Мы используем тот же процесс, что и выше, для преобразования данных, но процесс создания фрейма данных (DataFrame) добавляет пару дополнительных шагов.

Результатом является массив, который необходимо преобразовать во фрейм данных:

Следующим шагом будет присоединение этих данных обратно к исходному фрейму.

Вот пример:

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

Продвинутые подходы

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

Другой приятный аспект заключается в том, что автор статьи создал пакет для scikit-learn под названием category_encoders, который реализует многие из этих подходов. Это очень хороший инструмент, позволяющий взглянуть на проблему с другой точки зрения.

В первом примере мы попробуем выполнить кодирование обратной разницы (Backward Difference encoding).

Сначала мы получаем чистый фрейм данных и настраиваем BackwardDifferenceEncoder:

Интересно то, что результат не соответствует стандартным единицам и нулям, которые мы видели в предыдущих примерах кодирования.

Если мы попробуем полиномиальное кодирование (polynomial encoding), то получим другое распределение значений, используемых для кодирования столбцов:

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

Конвейеры scikit-learn

Цель этого раздела показать, как интегрировать особенности функций кодирования scikit-learn в простой конвейер (pipeline) построения модели.

Как упоминалось выше, категориальные кодировщики scikit-learn позволяют включать преобразование в ваши конвейеры, что позволяет упростить процесс построения модели и избежать некоторых ошибок. Я рекомендую это видео в качестве хорошего вступления. Оно послужило основой для изложенного ниже подхода.

Вот очень быстрый пример того, как включить OneHotEncoder и OrdinalEncoder в конвейер и использовать cross_val_score для анализа результатов:

Теперь, когда у нас есть данные, давайте создадим преобразователь (transformer) столбцов:

В этом примере показано, как применять разные типы кодировщиков для определенных столбцов.

Используем аргумент restder='passthrough' для передачи всех числовых значений через конвейер без каких-либо изменений.

Для модели мы используем простую линейную регрессию, а затем создаем конвейер:

Выполните перекрестную проверку (cross validation) 10 раз, используя отрицательную среднюю абсолютную ошибку (neg_mean_absolute_error) в качестве функции оценки.

Наконец, возьмите среднее из 10 значений, чтобы увидеть величину ошибки:

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

Заключение

Кодирование категориальных переменных - важный шаг в процессе анализа данных. Поскольку существует несколько подходов к кодированию переменных, важно понимать различные варианты и способы их реализации в ваших собственных наборах данных. В экосистеме науки о данных Python есть много полезных подходов к решению этих проблем. Я призываю вас помнить об этих идеях в следующий раз, когда вы обнаружите, что анализируете категориальные переменные.