Категориальные данные: one-hot кодирование в Python
Основные подходы к one-hot кодированию
One-hot кодирование преобразует категориальные признаки в бинарные векторы. Каждая категория становится отдельным столбцом со значением 1 для данной строки и 0 для остальных. Рассмотрим несколько вариантов реализации на Python.
Как выполнить one-hot кодирование с помощью sklearn OneHotEncoder?
Этот подход считается основным и наиболее эффективным, так как интегрируется в пайплайны и корректно обрабатывает неизвестные категории на новых данных.
import pandas as pd
from sklearn.preprocessing import OneHotEncoder
# Исходные данные
df = pd.DataFrame({'цвет': ['красный', 'синий', 'зеленый', 'синий']})
encoder = OneHotEncoder(sparse_output=False, handle_unknown='ignore')
encoded = encoder.fit_transform(df[['цвет']])
# Превращаем в DataFrame с названиями столбцов
result = pd.DataFrame(encoded, columns=encoder.get_feature_names_out(['цвет']))
print(result)
One hot python (one-hot кодирование в python)
цвет_зеленый цвет_красный цвет_синий 0 0.0 1.0 0.0 1 0.0 0.0 1.0 2 1.0 0.0 0.0 3 0.0 0.0 1.0
Преимущества:
- Обработка неизвестных категорий – параметр
handle_unknown='ignore'позволяет не выбрасывать ошибку при появлении новой категории в тесте. - Совместимость с Pipeline – можно использовать в цепочке преобразований вместе с масштабированием и моделью.
- Управление разреженностью – для больших данных
sparse_output=Trueэкономит память.
Проблема: результат – массив NumPy, столбцы не сразу удобны для анализа. Решение – преобразовать в DataFrame с помощью get_feature_names_out.
Типичная ошибка: забыть указать sparse_output=False и затем пытаться конкатенировать с pandas DataFrame. Решение – либо использовать sparse матрицу явно, либо конвертировать её в плотную.
Как сделать one-hot кодирование с pandas get_dummies?
Простой и наглядный способ, подходит для исследовательского анализа, но не интегрируется в пайплайны и не обрабатывает новые категории.
import pandas as pd
df = pd.DataFrame({'цвет': ['красный', 'синий', 'зеленый', 'синий']})
df_encoded = pd.get_dummies(df, columns=['цвет'])
print(df_encoded)
цвет_зеленый цвет_красный цвет_синий 0 False True False 1 False False True 2 True False False 3 False False True
Цель использования:
- Быстрая генерация фиктивных переменных для моделирования без дополнительных библиотек.
- Автоматическое создание имён столбцов на основе значений.
Проблема: при разных наборах категорий в тренировочных и тестовых данных возникает несовпадение столбцов. Решение – вручную выровнять столбцы с помощью reindex или использовать OneHotEncoder.
Другая ошибка: get_dummies возвращает булевы значения, а не числа 0/1. Для некоторых моделей (например, линейная регрессия) это не критично, но может вызвать неявное приведение типов. Решение – явно преобразовать в int: astype(int).
Как сделать one-hot кодирование вручную с помощью словаря и numpy?
Этот вариант полезен для понимания внутреннего механизма или при работе с очень большими разреженными данными без дополнительных библиотек.
import numpy as np
import pandas as pd
df = pd.DataFrame({'цвет': ['красный', 'синий', 'зеленый', 'синий']})
categories = sorted(df['цвет'].unique())
cat_to_idx = {cat: i for i, cat in enumerate(categories)}
n_rows = len(df)
n_cats = len(categories)
one_hot = np.zeros((n_rows, n_cats), dtype=int)
for row, cat in enumerate(df['цвет']):
one_hot[row, cat_to_idx[cat]] = 1
result = pd.DataFrame(one_hot, columns=categories)
print(result)
зеленый красный синий 0 0 1 0 1 0 0 1 2 1 0 0 3 0 0 1
Цель использования:
- Образовательная – понять, как работает one-hot.
- Специфические сценарии, когда нужно полностью контролировать отображение.
Проблема: при большом количестве категорий массив может быть огромным. Решение – использовать разреженные матрицы из scipy.sparse.
Ошибка: порядок категорий не сохраняется, если не отсортировать его явно. Решение – всегда определять порядок перед кодированием.
Как использовать category_encoders для one-hot кодирования?
Библиотека category_encoders предоставляет единый интерфейс для многих кодировщиков, включая OneHotEncoder с возможностью drop первого уровня (избегание мультиколлинеарности).
import pandas as pd
import category_encoders as ce
df = pd.DataFrame({'цвет': ['красный', 'синий', 'зеленый', 'синий']})
encoder = ce.OneHotEncoder(use_cat_names=True, drop_invariant=False)
encoded = encoder.fit_transform(df['цвет'])
print(encoded)
цвет_зеленый цвет_красный цвет_синий 0 0 1 0 1 0 0 1 2 1 0 0 3 0 0 1
Цель использования:
- Единый синтаксис для разных типов кодирования (target, ordinal и т.д.).
- Автоматическая обработка пропусков.
Проблема: при добавлении новых категорий в тестовых данных encoder выдаёт ошибку. Решение – использовать параметр handle_unknown (в версии 2.0+).
Ошибка: если не задать use_cat_names=True, имена столбцов будут сгенерированы как целые числа – сложнее интерпретировать.
Как сделать one-hot кодирование для меток классов с помощью Keras?
В задачах классификации часто преобразуют метки (целые числа) в one-hot векторы. Для этого удобна функция to_categorical из Keras.
import numpy as np
from tensorflow.keras.utils import to_categorical
labels = np.array([0, 1, 2, 1])
encoded = to_categorical(labels, num_classes=3)
print(encoded)
[[1. 0. 0.] [0. 1. 0.] [0. 0. 1.] [0. 1. 0.]]
Цель использования:
- Подготовка целевых переменных для нейронных сетей с категориальной кросс-энтропией.
- Простота и встроенная поддержка разреженных меток.
Проблема: если метки не последовательны (пропущены значения), num_classes нужно задавать явно, иначе он определится как max_label+1, что может быть меньше реального числа классов. Решение – передать список всех возможных классов.
Ошибка: to_categorical возвращает float32, что может быть неожиданно для целочисленных моделей. Решение – конвертировать astype(int) при необходимости.
Расширенные примеры one-hot кодирования
1. One-hot для нескольких столбцов и работа с разреженной матрицей
На практике часто кодируют сразу несколько категориальных признаков. Покажем, как это сделать с OneHotEncoder и конвертировать разреженную матрицу в DataFrame для анализа.
import pandas as pd
from sklearn.preprocessing import OneHotEncoder
# Данные с двумя категориальными столбцами
df = pd.DataFrame({
'цвет': ['красный', 'синий', 'зеленый', 'синий'],
'размер': ['S', 'M', 'L', 'M']
})
encoder = OneHotEncoder(sparse_output=True, handle_unknown='ignore')
encoded_sparse = encoder.fit_transform(df[['цвет', 'размер']])
print('Тип:', type(encoded_sparse))
print('Размер:', encoded_sparse.shape)
# Преобразуем в плотный DataFrame
df_encoded = pd.DataFrame(
encoded_sparse.toarray(),
columns=encoder.get_feature_names_out(['цвет', 'размер'])
)
print(df_encoded)
Тип:Размер: (4, 6) цвет_зеленый цвет_красный цвет_синий размер_L размер_M размер_S 0 0.0 1.0 0.0 0.0 0.0 1.0 1 0.0 0.0 1.0 0.0 1.0 0.0 2 1.0 0.0 0.0 1.0 0.0 0.0 3 0.0 0.0 1.0 0.0 1.0 0.0
2. Избегание ловушки фиктивных переменных (dummy trap)
Чтобы убрать один из столбцов (устранить идеальную мультиколлинеарность), используют параметр drop='first' в OneHotEncoder или drop_first=True в pd.get_dummies.
import pandas as pd
from sklearn.preprocessing import OneHotEncoder
df = pd.DataFrame({'цвет': ['красный', 'синий', 'зеленый', 'синий']})
# sklearn
enc_drop = OneHotEncoder(drop='first', sparse_output=False)
arr = enc_drop.fit_transform(df[['цвет']])
df_sk = pd.DataFrame(arr, columns=enc_drop.get_feature_names_out(['цвет']))
print('С drop first (sklearn):')
print(df_sk)
# pandas
df_pd = pd.get_dummies(df, columns=['цвет'], drop_first=True)
print('\nС drop first (pandas):')
print(df_pd)
С drop first (sklearn): цвет_красный цвет_синий 0 1.0 0.0 1 0.0 1.0 2 0.0 0.0 3 0.0 1.0 С drop first (pandas): цвет_синий цвет_зеленый 0 False False 1 True False 2 False True 3 True False
Примечание:
pandas с drop_first=True даёт булевы значения и порядок столбцов может отличаться; рекомендуется преобразовать в int и при необходимости отсортировать.3. Интеграция OneHotEncoder в Pipeline
Для автоматизации предобработки и масштабирования OneHotEncoder комбинируют с ColumnTransformer.
import pandas as pd
import numpy as np
from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.compose import ColumnTransformer
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
# Данные
X = pd.DataFrame({
'цвет': ['красный', 'синий', 'зеленый'],
'число': [10, 20, 30]
})
y = [0, 1, 0]
# Пайплайн
preprocessor = ColumnTransformer(
transformers=[
('cat', OneHotEncoder(sparse_output=False), ['цвет']),
('num', StandardScaler(), ['число'])
]
)
pipeline = Pipeline(steps=[
('preprocessor', preprocessor),
('classifier', LogisticRegression())
])
pipeline.fit(X, y)
preds = pipeline.predict(X)
print('Предсказания:', preds)
Предсказания: [0 1 0]
Объяснение: ColumnTransformer применяет one-hot кодирование к столбцу 'цвет' и стандартизацию к 'число'. Затем модель обучается на объединённых признаках.
4. Обратное преобразование one-hot в исходные категории
Метод inverse_transform OneHotEncoder восстанавливает исходные значения из бинарной матрицы.
import pandas as pd
from sklearn.preprocessing import OneHotEncoder
categories = ['A', 'B', 'C']
encoder = OneHotEncoder(sparse_output=False)
encoded = encoder.fit_transform(pd.DataFrame({'cat': categories}))
print('Закодировано:')
print(encoded)
# Обратное преобразование
decoded = encoder.inverse_transform(encoded)
print('\nВосстановлено:', decoded.flatten())
Закодировано: [[1. 0. 0.] [0. 1. 0.] [0. 0. 1.]] Восстановлено: ['A' 'B' 'C']
Примечание:
Обратное преобразование работает корректно только если матрица содержит ровно одну единицу на строку; если были удалены столбцы (drop='first'), результат будет неполным.5. One-hot с пропущенными значениями
Если в данных есть NaN, поведение разных кодировщиков различается. OneHotEncoder по умолчанию считает NaN отдельной категорией (если handle_unknown='ignore', то NaN кодируется как все нули).
import pandas as pd
from sklearn.preprocessing import OneHotEncoder
df = pd.DataFrame({'цвет': ['красный', None, 'синий', None]})
encoder = OneHotEncoder(sparse_output=False, handle_unknown='ignore')
encoded = encoder.fit_transform(df[['цвет']])
result = pd.DataFrame(encoded, columns=encoder.get_feature_names_out(['цвет']))
print(result)
цвет_красный цвет_синий 0 1.0 0.0 1 0.0 0.0 2 0.0 1.0 3 0.0 0.0
NaN превратились в строку из нулей – модель не получит информацию о пропуске. Если нужно явно кодировать пропуски, можно заменить NaN на специальную строку (например, 'missing') перед кодированием.
6. Работа с очень большим числом категорий
Когда категорий тысячи, one-hot может быть неэффективен. Но если всё же нужно, используйте разреженные матрицы. Пример с случайными данными (1000 строк, 500 категорий).
import numpy as np
import pandas as pd
from sklearn.preprocessing import OneHotEncoder
# Генерация данных: 1000 строк, 500 уникальных категорий
np.random.seed(42)
categories = [f'cat_{i}' for i in range(500)]
data = np.random.choice(categories, size=1000)
df = pd.DataFrame({'big_cat': data})
encoder = OneHotEncoder(sparse_output=True)
encoded = encoder.fit_transform(df[['big_cat']])
print('Размер разреженной матрицы:', encoded.shape)
print('Занимаемая память (байт):', encoded.data.nbytes)
# Сравнение с плотной
print('Плотная заняла бы (байт):', 1000 * 500 * 8)
Размер разреженной матрицы: (1000, 500) Занимаемая память (байт): 8000 Плотная заняла бы (байт): 4000000
Разреженная матрица экономит память, так как хранит только ненулевые элементы.