No history yet

Подготовка данных

Подготовка — ключ к успеху

Модели машинного обучения подобны двигателям: они могут быть невероятно мощными, но качество их работы напрямую зависит от качества «топлива», которым вы их снабжаете. В нашем случае топливо — это данные. Необработанные, «сырые» данные часто содержат ошибки, пропуски и форматы, которые алгоритмы просто не могут понять. Процесс преобразования этих данных в чистый, структурированный вид называется подготовкой данных, и это, возможно, самый важный этап в построении эффективной модели.

Мы будем работать с гипотетическим набором данных о недвижимости. Наша цель — подготовить его для модели, которая будет предсказывать стоимость дома. Для всех операций мы будем использовать две основные библиотеки экосистемы Python: Pandas для манипуляции данными и Scikit-Learn для их предобработки.

Первый взгляд с Pandas

Прежде чем что-то менять, нужно понять, с чем мы имеем дело. Pandas — идеальный инструмент для загрузки и первичного анализа данных. Давайте загрузим наш датасет и посмотрим на его структуру.

import pandas as pd
import numpy as np

# Создаем гипотетический DataFrame
data = {
    'Площадь': [120, 150, 90, np.nan, 200, 135],
    'Кол-во комнат': [3, 4, 2, 3, 5, 3],
    'Район': ['Центр', 'Север', 'Юг', 'Центр', 'Север', np.nan],
    'Состояние': ['Отличное', 'Хорошее', 'Требует ремонта', 'Хорошее', 'Отличное', 'Хорошее'],
    'Цена': [150000, 210000, 95000, 165000, 250000, 170000]
}
df = pd.DataFrame(data)

# Выводим основную информацию
print('Информация о DataFrame:')
df.info()

print('\nОписательная статистика:')
print(df.describe())

Метод .info() показывает типы данных каждого столбца и количество непропущенных значений. Мы сразу видим пропуски в столбцах «Площадь» и «Район». Метод .describe() дает нам базовую статистику (среднее, стандартное отклонение, медиану и т.д.) только для числовых признаков. Эта информация — наша отправная точка для дальнейшей очистки.

Чистка и преобразование

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

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

Самый простой способ — удалить строки с пропусками, но это может привести к потере ценной информации. Лучше заполнить их, используя разумную стратегию. Для числовых признаков, как «Площадь», часто используют среднее или медианное значение. Для категориальных, как «Район», — самое частое значение (моду). В Scikit-Learn для этого есть удобный инструмент — SimpleImputer.

from sklearn.impute import SimpleImputer

# Заполнение пропусков в числовом столбце медианой
num_imputer = SimpleImputer(strategy='median')
df['Площадь'] = num_imputer.fit_transform(df[['Площадь']])

# Заполнение пропусков в категориальном столбце модой
cat_imputer = SimpleImputer(strategy='most_frequent')
df['Район'] = cat_imputer.fit_transform(df[['Район']])

print(df.info())

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

Здесь важно различать два типа признаков:

  • Номинальные: Категории, не имеющие внутреннего порядка. Например, «Район» ('Центр', 'Север', 'Юг'). Нельзя сказать, что 'Север' > 'Центр'.
  • Порядковые: Категории с четко определенным порядком. Например, «Состояние» ('Требует ремонта' < 'Хорошее' < 'Отличное').

Для номинальных признаков идеально подходит OneHotEncoder, который создает новый бинарный столбец для каждой категории. Для порядковых — OrdinalEncoder, который присваивает каждой категории целое число в соответствии с ее порядком.

from sklearn.preprocessing import OneHotEncoder, OrdinalEncoder

# One-Hot кодирование для номинального признака 'Район'
ohe = OneHotEncoder(sparse_output=False, handle_unknown='ignore')
area_encoded = ohe.fit_transform(df[['Район']])
area_df = pd.DataFrame(area_encoded, columns=ohe.get_feature_names_out(['Район']))

# Ordinal кодирование для порядкового признака 'Состояние'
condition_order = ['Требует ремонта', 'Хорошее', 'Отличное']
ord_encoder = OrdinalEncoder(categories=[condition_order])
df['Состояние_кодированное'] = ord_encoder.fit_transform(df[['Состояние']])

# Объединяем результаты и удаляем исходные столбцы
df_processed = pd.concat([df.drop(['Район', 'Состояние'], axis=1), area_df], axis=1)

print(df_processed.head())

Разделение и масштабирование

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

Этот шаг предотвращает так называемую — одну из самых коварных ошибок в машинном обучении.

Lesson image

В Scikit-Learn это делается одной строкой с помощью train_test_split.

from sklearn.model_selection import train_test_split

X = df_processed.drop('Цена', axis=1)
y = df_processed['Цена']

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.25, random_state=42
)

Последний шаг — масштабирование признаков. Такие признаки, как «Площадь» (значения от 90 до 200) и «Кол-во комнат» (от 2 до 5), имеют совершенно разные масштабы. Для алгоритмов, основанных на расстоянии (например, k-NN или SVM), это может стать проблемой: признак с большим диапазоном значений будет оказывать непропорционально большое влияние на результат. Чтобы это исправить, признаки приводят к одному масштабу.

МетодОписаниеКогда использовать
Стандартизация (StandardScaler)Преобразует данные так, чтобы их среднее было равно 0, а стандартное отклонение — 1.Наиболее распространенный метод. Хорошо работает, когда данные имеют распределение, близкое к нормальному.
Нормализация (MinMaxScaler)Масштабирует данные в заданный диапазон, обычно [0, 1].Полезно, когда алгоритм требует данных в определенном диапазоне, или когда распределение не является нормальным. Чувствителен к выбросам.

Важно: скалер (например, StandardScaler) нужно обучать (fit) только на обучающей выборке, а затем применять (transform) и к обучающей, и к тестовой.

from sklearn.preprocessing import StandardScaler

# Выбираем только числовые столбцы для масштабирования
numeric_features = ['Площадь', 'Кол-во комнат']

scaler = StandardScaler()

# Обучаем скалер на обучающих данных и трансформируем их
X_train[numeric_features] = scaler.fit_transform(X_train[numeric_features])

# Применяем тот же скалер к тестовым данным
X_test[numeric_features] = scaler.transform(X_test[numeric_features])

print('Масштабированная обучающая выборка:')
print(X_train.head())

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

Quiz Questions 1/6

Какова основная цель разделения данных на обучающую и тестовую выборки?

Quiz Questions 2/6

У вас есть признак «Состояние» со значениями «Требует ремонта», «Хорошее» и «Отличное». Какой тип кодировщика из Scikit-Learn лучше всего подходит для преобразования этого признака в числовой формат?