Преобразование данных. Часть 1 | Анализ и обработка данных

Преобразование данных. Часть 1

Все курсы > Анализ и обработка данных > Занятие 8 (часть 1)

Для многих моделей машинного обучения важно, чтобы количественные данные имели одинаковый масштаб (same scale). Это справедливо для

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

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

Сегодняшнее занятие в большей степени посвящено способам и инструментам преобразования данных и выявления выбросов. Практикой их применения мы займемся на последующих занятиях.

Откроем ноутбук к этому занятию

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

Скачаем и подгрузим данные о недвижимости в Бостоне.

Подготовим данные.

Визуализируем распределения с помощью гистограммы.

преобразование количественных переменных, датасет о недвижимости в Бостоне

Посмотрим на основные статистические показатели.

метод .describe(), датасет о недвижимости в Бостоне

Линейные и нелинейные преобразования

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

Линейные преобразования (linear transformation) не меняют структуру распределения, нелинейные преобразования (non-linear transformation) — меняют.

Возьмем скошенное вправо распределение LSTAT и применим к нему один из способов масштабирования, стандартизацию, и нелинейное преобразование Бокса-Кокса (смысл этих преобразований мы подробно рассмотрим ниже).

пример стандартизации и степенного преобразования

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

Замечу, что в основном сегодня мы будем пользоваться модулем preprocessing⧉ библиотеки sklearn или трансформационными инструментами⧉ Scipy.

Добавление выбросов

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

добавление выбросов в данные

Посмотрим на данные с выбросами и без.

точечная диаграмма: данные с выбросами и без

Линейные преобразования (масштабирование)

Рассмотрим несколько способов масштабирования признаков (feature scaling).

Стандартизация

Если данные имеют нормальное или близкое к нормальному распределение (что желательно для многих моделей ML), имеет смысл прибегнуть к стандартизации (standartazation): то есть приведению к нулевому среднему значению и единичному СКО (так называемое стандартное нормальное распределение). Приведем формулу.

$$ x’ = \frac{x-\mu}{\sigma} $$

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

Стандартизация вручную

Замечу, что часто бывает удобно стандартизировать данные без использования класса sklearn.

стандартизация данных вручную

StandardScaler

Преобразование данных

Точно такой же результат можно получить через класс StandardScaler модуля preprocessing библиотеки sklearn. Создадим объект этого класса и применим метод .fit().

При вызове метода .fit() алгоритм рассчитывает среднее арифметическое и СКО каждого из столбцов. Их можно посмотреть через соответствующие атрибуты.

Метод .transform() соответственно использует рассчитанные значения среднего и СКО для стандартизации данных.

класс StandardScaler библиотеки sklearn

Метод .fit_transform() сразу вычисляет статистические показатели и применяет их для масштабирования данных.

Визуализация преобразования

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

Применим эти функции к стандартизированным данным.

точечная диаграмма: стандартизация данных с выбросами и без
точечная диаграмма: стандартизация данных с выбросами и без

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

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

Хорошая иллюстрация этого факта есть на сайте библиотеки sklearn⧉.

Обратное преобразование

Вернуть исходный масштаб можно с помощью метода .inverse_transform().

Иногда возникает вопрос, почему исходные и преобразованные к исходному виду данные не будут идентичными.

Это связано лишь с особенностями округления (как видно ниже различия минимальны).

обратное преобразование после стандартизации

Одновременно не всегда бывает понятно, зачем использовать класс StandardScaler, когда быстрее написать код самостоятельно.

Проблема утечки данных

В ответах на вопросы к занятию по классификации (в рамках вводного курса), мы уже поднимали вопрос применения стандартизации к обучающей и тестовой выборкам и упомянули проблему утечки данных (data leakage). Рассмотрим этот вопрос еще раз.

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

Для того чтобы тестовые данные никак не влияли на обучающую часть, нужно:

  • рассчитать среднее и СКО обучающей выборки
  • отмасштабировать обучающие данные
  • обучить на них модель
  • использовать ранее рассчитанные среднее и СКО для масштабирования тестовых данных
  • сделать прогноз на отмасштабированных тестовых данных и оценить качество модели

Именно такое разделение и обеспечивают методы .fit() и .transform().

Перейдем к практике.

данные о недвижимости в Калифорнии

Применение пайплайна

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

Пайплайн (pipeline) последовательно применяет заданные преобразования данных (transformer) и выдает прогноз или метрику последнего по порядку инструмента, как правило, класса модели (estimator).

В нашем случае инструмента два: StandardScaler (transformer) и LinearRegression (estimator). Вначале рассмотрим более простой с точки зрения синтаксиса способ.

Класс make_pipeline

Создадим с помощью класса make_pipeline объект-контейнер, в который поместим необходимые нам инструменты.

Применим метод .fit() к объекту pipe и используем обучающую выборку. Этот метод:

  • вызывает соответствующие методы .fit() и .transform() класса StandardScaler, т.е. рассчитывает среднее и СКО и масштабирует данные
  • затем вызовет метод .fit() класса LinearRegression, обучит модель на преобразованных данных и «запомнит» коэффициенты

Теперь если мы применим к объекту pipe метод .predict() и передадим ему признаки тестовой части, то пайплайн вначале стандартизирует выборку с помощью рассчитанных ранее среднего и СКО обучающей выборки, а затем сделает прогноз.

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

Аналогично, мы можем применить метод .score() и передать ему тестовую выборку. Этот метод выполнит масштабирование, обучит модель, сделает прогноз и посчитает метрику качества.

Замечу, что метод .score() применим только в том случае, если последний класс внутри пайплайна располагает таким методом. В нашем случае, для класса LinearRegression метод .score() задан и выдает коэффициент детерминации $R^2$.

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

Класс make_pipeline является упрощенной версией класса Pipeline.

Класс Pipeline

Для того чтобы создать объект класса Pipeline, этому классу нужно передать кортежи из названия инструмента и соответствующего класса.

Обратите внимание на параметр verbose. Он используется во многих классах и функциях. Изначально, verbose по-английский означает «склонный к многословности, болтливый [человек]», применительно к программированию — это детальный вывод хода выполнения программы.

Нормализация среднего

Нормализация среднего (mean normalization) предполагает деление разности между значением и средним признака не на СКО, а на диапазон от минимального до максимального значения.

$$ x’ = \frac{x-\mu}{x_{max}-x_{min}} $$

Перейдем к другим способам масштабирования.

Приведение к диапазону

Приведение признаков к заданному диапазону (scaling features to a range) является альтернативой стандартизации в тех случаях, когда нормальное распределение не является условием для обучения алгоритма. Рассмотрим два инструмента: MinMaxScaler и MaxAbsScaler.

MinMaxScaler

MinMax Scaler приводит данные к заданному диапазону (по умолчанию к промежутку от 0 до 1). Приведем формулу.

$$ x’ = \frac{x-x_{min}}{x_{max}-x_{min}} $$

Если мы хотим привести данные к произвольному диапазону [a, b], то можем воспользоваться общей формулой.

$$ x’ = a+\frac{(x-x_{min})(b-a)}{x_{max}-x_{min}} $$

Дополнительно замечу, что при работе с изображениями, если скажем на черно-белой фотографии каждый пиксель имеет диапазон от 0 до 255, то для приведения всех пикселей к диапазону от 0 до 1 достаточно разделить каждое значение на 255.

Применим класс MinMaxScaler к нашим данным.

Визуально оценим результат.

MinMaxScaler: точечная диаграмма
MinMaxScaler: гистограмма

Этот метод также чувствителен к выбросам и при их наличии не обеспечивает⧉ единого масштаба признаков.

MaxAbsScaler

Стандартизация разреженных данных

Работая с рекомендательными системами, мы увидели, что данные могут храниться в разреженных матрицах (sparse matrices). Приведем простой пример.

MaxAbsScaler: разреженные данные

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

стандартизация разреженных данных

Таким образом мы испортим наши данные. Для того чтобы этого избежать можно использовать MaxAbsScaler.

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

Формула и простой пример

Приведем формулу.

$$ x’ = \frac{x}{|x_{max}|} $$

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

В качестве примера разберем первый столбец. Максимальным значением по модулю будет два и именно на это число мы делим каждое значение признака.

Отметим некоторые особенности преобразования MaxAbsScaler:

  • нулевые значения сохраняются
  • только положительные значения столбца приводятся к диапазону от 0 до 1 и здесь MaxAbsScaler работает так же, как и MinMaxScaler
  • только отрицательные значения приводятся к диапазону от −1 до 0
  • положительные и отрицательные значения — к диапазону от −1 до 1
Разреженная матрица и MaxAbsScaler

Применим MaxAbsScaler к разреженной матрице.

применение MaxAbsScaler к разреженной матрице
Матрица csr и MaxAbsScaler

Как мы знаем, разреженные матрицы удобно хранить в формате сжатого хранения строкой (сompressed sparse row, csr), и мы можем применить MaxAbsScaler непосредственно к данным в этом формате.

MaxAbsScaler также чувствителен к выбросам⧉.

RobustScaler

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

$$ x = \frac{x-Q_1(x)}{Q_3(x)-Q_1(x)} $$

Применим класс RobustScaler.

Посмотрим на преобразование на графике.

RobustScaler: точечная диаграмма
RobustScaler: гистограмма

RobustScaler не приводит данные строго к одному диапазону и не меняет структуру распределения (и в частности не изменяет расстояние между основной массой данных и выбросами).

Класс Normalizer

Класс Normalizer, в отличие от предыдущих инструментов, по умолчанию приводит наблюдения (то есть строки, а не столбцы датафрейма) к единичной норме (или длине вектора, равной единице, unit norm, unit vector) или нормализует (normalizes) их.

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

Понятие нормы вектора

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

Это число часто рассматривают как длину вектора от начала координат до конца вектора. Причем эту длину или расстояние можно измерять по-разному.

Рассмотрим вероятно наиболее распространенное Евклидово расстояние (Eucledean distance) или как ещё говорят L2 норму (L2 norm) вектора $\textbf{x}$ с координатами ${x_1, x_2,…, x_n} $ (термины длины, расстояния и нормы часто оказываются взаимозаменяемы).

$$ || \textbf{x} ||_2 = \sqrt{x_1^2 + x_2^2 + … + x_n^2} $$

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

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

Это и есть L2 нормализация.

длина вектора

L2 нормализация

Возьмём простой двумерный массив данных, вручную выполним построчную L2 нормализацию, а затем с помощью класса Normalizer проверим результат.

Графически конец каждого L2 нормализованного вектора оказывается на единичной окружности (то есть окружность с радиусом, равным единице).

L2 нормализация и единичная окружность

Этот метод подходит для алгоритмов, основанных на расстоянии между векторами. В частности, вспомним формулу косинусного сходства.

$$ \cos{ \theta } = \frac{\textbf{a} \cdot \textbf{b}}{|| \textbf{a} ||_2 || \textbf{b} ||_2} $$

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

Опасность нормализации по строкам

С точки зрения масштабирования данных у такого подхода есть один недостаток. Нормализация по строкам разрушает связи внутри признаков. Рассмотрим массив, в котором строки это люди, а столбцы — данных о них (в частности, рост, вес и возраст).

Как мы видим, обоим людям по 50 лет. Проведем L2 нормализацию по строкам.

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

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

L1 нормализация

Как уже было сказано длину вектора не обязательно измерять по формуле Евклидова расстояния. Можно воспользоваться формулой расстояния городских кварталов (Manhattan distance, taxicab distance) или L1 нормой (L1 norm).

$$ || \textbf{x} ||_1 = |x_1| + |x_2| + … + |x_n| $$

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

Теперь к единичной норме приведена сумма модулей координат вектора.

Сравним результат с объектом класса Normalizer.

Теперь выведем L1 нормализованные векторы на графике и посмотрим, как рассчитывалось расстояние до первого вектора.

L1 нормализация

Из графика выше становится очевидно, почему это расстояние L1 (то есть сумма черного и красного векторов, для L1 нормализованного вектора равная единице) называется Manhattan distance или taxicab distance. Водителю такси на Манхэттене, основанном на гипподамовой системе с прямоугольными кварталами, чтобы попасть из точки А в точку Б пришлось бы двигаться строго перпендикулярными отрезками.

Расстояние Минковского

Обобщением Евклидова расстояния и расстояния городских кварталов будет расстояние Минковского (Minkowski distance).

$$ || \textbf{x} ||_p = \left( \sum_{i=1}^n { |x_i|^p } \right) ^{\frac{1}{p}} $$

где $p=1$ и $p=2$ — это соответственно метрика городских кварталов и Евклидово расстояние.

Расстояние Чебышёва

Что интересно, если p стремится к бесконечности, то формула расстояния принимает вид.

$$ \lim_{p\to\infty} \left( \sum_{i=1}^n { |x_i|^p } \right) ^{\frac{1}{p}} = \max_{i=1}^n | x_i | $$

Такое расстояние называется расстоянием Чебышёва (Chebyshev distance). По сути для расчета этого расстояния мы берем наибольшую по модулю координату вектора.

Для нормализации векторов по расстоянию Чебышёва классу Normalizer нужно передать параметр norm = ‘max’.

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

нормализация Чебышёва

Про терминологию

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

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

Перейдем к нелинейным преобразованиям.