На предыдущем этапе мы провели первичный осмотр данных. Теперь мы переходим к активным действиям над структурой датасета. Мы научимся избавляться от дубликатов, обрабатывать сложные текстовые поля с помощью регулярных выражений, заполнять пропущенные значения на основе математических стратегий и трансформировать целевую переменную для оптимизации обучения линейных моделей.
Первый шаг в предобработке — избавление от избыточной информации, которая не несет математической пользы или может исказить процесс обучения.
dataset.drop_duplicates() (в нашем случае было удалено около 80 идентичных записей).'ID объявления', 'Тип' (почти везде дублирует категорию квартиры), 'Адрес' (основная географическая привязка уже заложена в близости к станциям метро) и 'Телефоны'.dataset.rename(columns={'Количество комнат' : 'Комнат', 'Площадь...': 'Площадь'}).При выгрузке данных числовые показатели часто сохраняются в смешанном текстовом формате. Яркий пример — признак «Комнат». Вместо простых чисел на входе мы имеем записи вида: «1», «2», «2, изолированные», «3, смежные».
Алгоритм обработки признака «Комнат»:
dataset['Комнат'].astype(str).str[0], и конвертируем его в числовой формат с помощью pd.to_numeric().1.00 вместо 1). Это происходит из-за наличия пропусков (NaN). Стандартные целочисленные типы (int64) в NumPy не умеют хранить пустое значение NaN. Если в столбце есть хоть один пропуск, Pandas приводит весь столбец к типу float.dataset['Студия'] = np.where(pd.isna(dataset['Комнат']), 1, 0), а исходный пропуск в столбце комнат заменяем единицей. Теперь, когда пропусков в столбце «Комнат» не осталось, мы можем преобразовать его к чистому целочисленному типу: .astype(int).Признак «Площадь» в исходном виде хранит информацию в виде сложных конструкций: «41.5/20.0/10.0» (общая/жилая/кухня). Нам для обучения модели необходим единый числовой показатель общей площади.
Чтобы извлечь нужное значение, мы пишем кастомную Python-функцию, использующую регулярные выражения (библиотека re):
def area(row):
return re.search(r'([0-9]+)’, row[‘Площадь’]).group(1)
С помощью метода apply(area, axis=1) мы построчно применяем эту логику ко всему датасету, извлекая первую встреченную группу цифр (общую площадь) и приводя столбец к целому числу.
Пропущенные значения (NaN) — это «дыры» в выборке. Большинство моделей машинного обучения не умеют работать с ними напрямую. Подход к работе с пропусками зависит от природы их появления:
| Тип пропусков | Описание | Стратегия обработки |
|---|---|---|
| MCAR (Completely at Random) | Пропуски абсолютно случайны (например, технический сбой). | Удаление строк (если пропусков < 5%) или импутация средним/медианой. |
| MAR (at Random) | Пропуск зависит от других известных признаков (например, мужчины реже заполняют опросники). | Заполнение с использованием взаимосвязей (например, k-NN Imputer или MICE). |
| MNAR (Not at Random) | Пропуск зависит от самого скрытого значения (например, люди с высокими доходами их скрывают). | Сложный случай, требующий экспертных оценок, добавления бинарных признаков-индикаторов пропусков. |
fillna('Unknown') или fillna(0). Применяется в основном для категорий, сохраняя объем данных.mean() (среднее) используется для числовых признаков с симметричным распределением без сильных выбросов.median() (медиана) — стандартный выбор для числовых шкал, содержащих выраженные выбросы (медиана к ним устойчива).mode() (мода) — для категориальных признаков.KNNImputer (поиск аналогичных объектов по методу k-ближайших соседей) или IterativeImputer (MICE-моделирование).Целевая переменная (наш «учитель») требует особого контроля. Ошибки на этапе ее подготовки сделают модель неработоспособной.
Исходное поле цены очищается от лишнего текста (упоминаний залога, коммунальных платежей, валюты) при помощи регулярных выражений аналогично обработке площади и переводится в целочисленный формат.
В задачах регрессии (особенно при оценке цен, доходов, населения) распределение целевой переменной практически всегда имеет правую (положительную) скошенность (right-skewed distribution). Это означает, что большинство объектов сосредоточено в низком и среднем ценовом сегменте, но присутствует длинный «хвост» из экстремально дорогих элитных вариантов (выбросов).
Поскольку линейная регрессия пытается минимизировать совокупную ошибку, крупные выбросы будут перетягивать линию регрессии на себя, ухудшая качество прогнозов на типичных недорогих объектах.
Стандартным математическим решением этой проблемы является логарифмирование целевой переменной: \(y_{\text{transformed}} = \log(y)\). Применение логарифма «сжимает» длинный правый хвост распределения, приближая его форму к симметричному нормальному распределению. Это стабилизирует работу модели и повышает итоговую точность прогнозирования.
drop_duplicates().drop().rename() для приведения имен колонок к удобному лаконичному виду.NaN) преобразует целочисленные столбцы к типу float. Ограничения библиотеки NumPy.re).apply() для построчного применения кастомных Python-функций к DataFrame (параметр axis=1).dropna()) и удаление столбцов целиком при критическом количестве пропусков.KNNImputer) или итерационных моделей (MICE / IterativeImputer).was_missing) как ценного признака для алгоритма.