Кодирование категорий: OneHotEncoder, LabelEncoder

Кодирование категорий: OneHotEncoder, LabelEncoder Scikit-learn

Привет! Ты уже знаешь, что модели машинного обучения работают только с числами. Но в реальных данных часто встречаются текстовые категории: «красный», «синий», «зелёный» или «клиент ушёл» / «клиент остался». Как быть?

В этой статье мы разберём:

  • Что такое категориальные данные
  • LabelEncoder — кодирование меток в числа
  • OneHotEncoder — создание бинарных столбцов
  • Когда и как использовать каждый метод
  • Как избежать распространённых ошибок

Что нужно знать перед началом

Для этого урока тебе понадобится:

  • Установленный scikit-learn (pip install scikit-learn)
  • Базовое понимание машинного обучения
  • Знание, что такое признаки (X) и целевая переменная (y)

Совет: Кодирование категорий — обязательный этап предобработки данных. Без него модели не смогут работать с текстовыми данными!

Основная часть

Что такое категориальные данные?

Категориальные данные — это данные, которые принимают ограниченное количество значений (категорий). Они бывают двух типов:

ТипОписаниеПример
НоминальныеНет порядка между значениямиЦвет: «красный», «синий», «зелёный»
ПорядковыеЕсть логический порядокРазмер: «S», «M», «L», «XL»

Совет: Различие важно — для номинальных данных нужен OneHotEncoder, для порядковых можно использовать LabelEncoder.

LabelEncoder — простое кодирование в числа

LabelEncoder заменяет каждую категорию на целое число (от 0 до N-1).

from sklearn.preprocessing import LabelEncoder

# Данные
colors = ['красный', 'синий', 'красный', 'зелёный', 'синий']

# Создаём и применяем
le = LabelEncoder()
encoded = le.fit_transform(colors)

print("Оригинал:", colors)
print("Закодировано:", encoded)
print("Классы:", le.classes_)

Результат:

Оригинал: ['красный', 'синий', 'красный', 'зелёный', 'синий']
Закодировано: [2 0 2 1 0]
Классы: ['зелёный', 'синий', 'красный']

Совет: LabelEncoder удобен для целевой переменной (y). Для признаков с несколькими категориями лучше использовать OneHotEncoder.

Где использовать LabelEncoder

Для целевой переменной (y)

В задачах классификации целевая переменная часто задана текстом (например, «Yes»/«No»). LabelEncoder превращает её в 0/1 :

from sklearn.preprocessing import LabelEncoder

y = ['Yes', 'No', 'No', 'Yes', 'Yes']
le = LabelEncoder()
y_encoded = le.fit_transform(y)

print(f"Целевая переменная: {y_encoded}")
print(f"Классы: {le.classes_}")  # ['No', 'Yes']

Для порядковых признаков

Если категории имеют порядок («Low», «Medium», «High»), LabelEncoder сохранит этот порядок в числах:

sizes = ['M', 'L', 'S', 'M', 'XL']
le = LabelEncoder()
sizes_encoded = le.fit_transform(sizes)
print(sizes_encoded)  # [1 2 0 1 3]
# S->0, M->1, L->2, XL->3 — порядок сохраняется

Совет: Для порядковых данных LabelEncoder подходит, потому что числа отражают естественный порядок.

OneHotEncoder — бинарное кодирование

OneHotEncoder создаёт отдельный бинарный столбец (0 или 1) для каждой категории.

import pandas as pd
from sklearn.preprocessing import OneHotEncoder

# Данные
colors = pd.DataFrame({'Цвет': ['красный', 'синий', 'красный', 'зелёный']})

# Создаём и применяем
ohe = OneHotEncoder(sparse_output=False)
encoded = ohe.fit_transform(colors)

print("Оригинал:")
print(colors)
print("\nЗакодировано (One-Hot):")
print(encoded)
print(f"Категории: {ohe.categories_}")

Результат:

Оригинал:
     Цвет
0  красный
1   синий
2  красный
3  зелёный

Закодировано (One-Hot):
[[0. 0. 1.]
 [0. 1. 0.]
 [0. 0. 1.]
 [1. 0. 0.]]
Категории: [array(['зелёный', 'синий', 'красный'], dtype=object)]

Совет: Параметр sparse_output=False возвращает обычную матрицу (не разреженную). Это удобно для просмотра.

Сравнение OneHotEncoder и LabelEncoder

ХарактеристикаLabelEncoderOneHotEncoder
РезультатОдна колонка с числамиN колонок с 0/1
ПорядокСоздаёт искусственный порядокНе создаёт порядок
РазмерностьНе увеличиваетУвеличивает (N категорий)
Когда использоватьЦелевая переменная, порядковые данныеНоминальные признаки
Пример['No','Yes'] → [0,1]['красный','синий'] → [[1,0],[0,1]]

Совет: OneHotEncoder не создаёт ложного порядка между категориями, что критично для номинальных данных.

Полный пример: подготовка данных для модели

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import OneHotEncoder, LabelEncoder
from sklearn.linear_model import LogisticRegression

# 1. Создаём данные
data = {
    'Город': ['Москва', 'СПб', 'Казань', 'Москва', 'СПб', 'Казань'],
    'Возраст': [25, 30, 22, 35, 28, 40],
    'Подписка': ['Yes', 'No', 'Yes', 'Yes', 'No', 'No']
}
df = pd.DataFrame(data)

print("Исходные данные:")
print(df)

# 2. Разделяем X и y
X = df[['Город', 'Возраст']]
y = df['Подписка']

# 3. Кодируем категориальные признаки (OneHotEncoder)
ohe = OneHotEncoder(sparse_output=False)
X_encoded = ohe.fit_transform(X[['Город']])
X_encoded_df = pd.DataFrame(X_encoded, columns=ohe.get_feature_names_out(['Город']))

# Объединяем с числовыми признаками
X_final = pd.concat([X_encoded_df, X[['Возраст']].reset_index(drop=True)], axis=1)

print("\nПризнаки после OneHotEncoder:")
print(X_final)

# 4. Кодируем целевую переменную (LabelEncoder)
le = LabelEncoder()
y_encoded = le.fit_transform(y)

print(f"\nЦелевая переменная (закодирована): {y_encoded}")
print(f"Классы: {le.classes_}")

# 5. Обучаем модель
X_train, X_test, y_train, y_test = train_test_split(
    X_final, y_encoded, test_size=0.3, random_state=42
)

model = LogisticRegression()
model.fit(X_train, y_train)
accuracy = model.score(X_test, y_test)
print(f"\nТочность модели: {accuracy:.2%}")

Когда использовать альтернативы

Проблема высокой размерности

Если в категориальном признаке 100+ уникальных значений, OneHotEncoder создаст 100 колонок — это много памяти. Рассмотри:

  • BinaryEncoder — кодирует категории в битовые строки (экономит память) 
  • Target Encoding — заменяет категорию на среднее целевой переменной (требует аккуратности) 

Для деревьев решений

Random Forest и Gradient Boosting не требуют OneHotEncoder — они хорошо работают с LabelEncoder.

# Для деревьев можно использовать просто LabelEncoder
for col in ['Город', 'Пол']:
    X[col] = LabelEncoder().fit_transform(X[col])

Задачи для закрепления

Задача 1. Что такое категориальные данные и какие они бывают двух типов?

Задача 2. Какой метод кодирования подходит для целевой переменной (y)?

Задача 3. Создай OneHotEncoder для признака ['A', 'B', 'C', 'A', 'B'].

Задача 4. В чём проблема использования LabelEncoder для номинальных признаков?

Задача 5. Когда OneHotEncoder может быть неэффективным?

Ответы:

Задача 1.

Категориальные данные — данные, принимающие ограниченное число значений. Бывают номинальные (без порядка) и порядковые (с порядком).

Задача 2.

LabelEncoder. Он преобразует текстовые метки в числа 0, 1, 2.

Задача 3.

from sklearn.preprocessing import OneHotEncoder
import numpy as np

data = np.array(['A', 'B', 'C', 'A', 'B']).reshape(-1, 1)
ohe = OneHotEncoder(sparse_output=False)
encoded = ohe.fit_transform(data)
print(encoded)

Задача 4.

LabelEncoder создаёт искусственный порядок (A=0, B=1, C=2). Модель может решить, что C > B > A, хотя для номинальных данных это не так.

Задача 5.

При большом количестве уникальных категорий (100+). OneHotEncoder создаёт 100 колонок, что увеличивает память и может ухудшить работу модели.

Нюансы и подводные камни

Unseen categories в тестовых данных

Если в тестовых данных появится категория, которой не было в обучении, OneHotEncoder по умолчанию выдаст ошибку. Решение: используй параметр handle_unknown='ignore'.

ohe = OneHotEncoder(handle_unknown='ignore', sparse_output=False)

Дамми-ловушка (Dummy Trap)

При использовании OneHotEncoder без drop='first' может возникнуть мультиколлинеарность. Решение: drop='first' убирает один столбец.

ohe = OneHotEncoder(drop='first', sparse_output=False)

LabelEncoder для нескольких колонок

LabelEncoder не предназначен для нескольких колонок одновременно. Для этого лучше использовать OrdinalEncoder:

from sklearn.preprocessing import OrdinalEncoder

encoder = OrdinalEncoder()
X_encoded = encoder.fit_transform(X[['Город', 'Пол']])

Частые ошибки и как их избежать

Ошибка 1: LabelEncoder для номинальных признаков

# Неправильно — создаёт ложный порядок
X['Город'] = LabelEncoder().fit_transform(X['Город'])

# Правильно — используй OneHotEncoder
ohe = OneHotEncoder(sparse_output=False)
X_encoded = ohe.fit_transform(X[['Город']])

Ошибка 2: OneHotEncoder для целевой переменной

# Неправильно — y становится матрицей
y_encoded = OneHotEncoder().fit_transform(y.to_frame())

# Правильно — используй LabelEncoder
y_encoded = LabelEncoder().fit_transform(y)

Ошибка 3: Не разделил данные перед кодированием

# Сначала кодируем всё, потом разделяем (утечка данных)
ohe.fit_transform(X)
X_train, X_test = train_test_split(X)

# Сначала разделяем, потом кодируем
X_train, X_test = train_test_split(X)
ohe.fit(X_train)
X_train_enc = ohe.transform(X_train)
X_test_enc = ohe.transform(X_test)

Шпаргалка

Что нужноКак пишется
LabelEncoderLabelEncoder()
OneHotEncoderOneHotEncoder(sparse_output=False)
Кодировать целевую переменнуюle.fit_transform(y)
Кодировать признакиohe.fit_transform(X[['колонка']])
Игнорировать новые категорииhandle_unknown='ignore'
Убрать дамми-ловушкуdrop='first'
Увидеть названия колонокohe.get_feature_names_out()

Заключение

Сегодня мы научились:

  • Различать номинальные и порядковые категориальные данные
  • Использовать LabelEncoder для целевой переменной и порядковых признаков
  • Использовать OneHotEncoder для номинальных признаков
  • Избегать ошибок при кодировании
  • Понимать, когда какой метод применять

Запомни главное правило:

Запомни главное правило:

LabelEncoder → для y (целевая переменная)
OneHotEncoder → для X (номинальные признаки)

КВИЗ

Что дальше?

Оцените статью
IMI-DS - PYTHON LERNEN
Содержание
Оглавление ×