Spec-Zone.ru › scikit-learn

Примечание

Перейти в конец, чтобы скачать полный код примера или запустить его в браузере с помощью JupyterLite или Binder.

Сравнение влияния различных методов масштабирования на данные с выбросами

Признак 0 (медианный доход в районе) и признак 5 (средняя заполняемость домов) набора данных California Housing имеют очень разные масштабы и содержат несколько очень больших выбросов. Эти две особенности затрудняют визуализацию данных и, что важнее, могут ухудшить качество предсказаний многих алгоритмов машинного обучения. Немасштабированные данные также могут замедлить или даже сделать невозможной сходимость многих оценщиков, основанных на градиентном спуске.

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

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

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

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

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

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

# Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause

import matplotlib as mpl
import numpy as np
from matplotlib import cm
from matplotlib import pyplot as plt

from sklearn.datasets import fetch_california_housing
from sklearn.preprocessing import (
    MaxAbsScaler,
    MinMaxScaler,
    Normalizer,
    PowerTransformer,
    QuantileTransformer,
    RobustScaler,
    StandardScaler,
    minmax_scale,
)

dataset = fetch_california_housing()
X_full, y_full = dataset.data, dataset.target
feature_names = dataset.feature_names

feature_mapping = {
    "MedInc": "Median income in block",
    "HouseAge": "Median house age in block",
    "AveRooms": "Average number of rooms",
    "AveBedrms": "Average number of bedrooms",
    "Population": "Block population",
    "AveOccup": "Average house occupancy",
    "Latitude": "House block latitude",
    "Longitude": "House block longitude",
}

# Take only 2 features to make visualization easier
# Feature MedInc has a long tail distribution.
# Feature AveOccup has a few but very large outliers.
features = ["MedInc", "AveOccup"]
features_idx = [feature_names.index(feature) for feature in features]
X = X_full[:, features_idx]
distributions = [
    ("Unscaled data", X),
    ("Data after standard scaling", StandardScaler().fit_transform(X)),
    ("Data after min-max scaling", MinMaxScaler().fit_transform(X)),
    ("Data after max-abs scaling", MaxAbsScaler().fit_transform(X)),
    (
        "Data after robust scaling",
        RobustScaler(quantile_range=(25, 75)).fit_transform(X),
    ),
    (
        "Data after power transformation (Yeo-Johnson)",
        PowerTransformer(method="yeo-johnson").fit_transform(X),
    ),
    (
        "Data after power transformation (Box-Cox)",
        PowerTransformer(method="box-cox").fit_transform(X),
    ),
    (
        "Data after quantile transformation (uniform pdf)",
        QuantileTransformer(
            output_distribution="uniform", random_state=42
        ).fit_transform(X),
    ),
    (
        "Data after quantile transformation (gaussian pdf)",
        QuantileTransformer(
            output_distribution="normal", random_state=42
        ).fit_transform(X),
    ),
    ("Data after sample-wise L2 normalizing", Normalizer().fit_transform(X)),
]

# scale the output between 0 and 1 for the colorbar
y = minmax_scale(y_full)

# plasma does not exist in matplotlib < 1.5
cmap = getattr(cm, "plasma_r", cm.hot_r)


def create_axes(title, figsize=(16, 6)):
    fig = plt.figure(figsize=figsize)
    fig.suptitle(title)

    # define the axis for the first plot
    left, width = 0.1, 0.22
    bottom, height = 0.1, 0.7
    bottom_h = height + 0.15
    left_h = left + width + 0.02

    rect_scatter = [left, bottom, width, height]
    rect_histx = [left, bottom_h, width, 0.1]
    rect_histy = [left_h, bottom, 0.05, height]

    ax_scatter = plt.axes(rect_scatter)
    ax_histx = plt.axes(rect_histx)
    ax_histy = plt.axes(rect_histy)

    # define the axis for the zoomed-in plot
    left = width + left + 0.2
    left_h = left + width + 0.02

    rect_scatter = [left, bottom, width, height]
    rect_histx = [left, bottom_h, width, 0.1]
    rect_histy = [left_h, bottom, 0.05, height]

    ax_scatter_zoom = plt.axes(rect_scatter)
    ax_histx_zoom = plt.axes(rect_histx)
    ax_histy_zoom = plt.axes(rect_histy)

    # define the axis for the colorbar
    left, width = width + left + 0.13, 0.01

    rect_colorbar = [left, bottom, width, height]
    ax_colorbar = plt.axes(rect_colorbar)

    return (
        (ax_scatter, ax_histy, ax_histx),
        (ax_scatter_zoom, ax_histy_zoom, ax_histx_zoom),
        ax_colorbar,
    )


def plot_distribution(axes, X, y, hist_nbins=50, title="", x0_label="", x1_label=""):
    ax, hist_X1, hist_X0 = axes

    ax.set_title(title)
    ax.set_xlabel(x0_label)
    ax.set_ylabel(x1_label)

    # The scatter plot
    colors = cmap(y)
    ax.scatter(X[:, 0], X[:, 1], alpha=0.5, marker="o", s=5, lw=0, c=colors)

    # Removing the top and the right spine for aesthetics
    # make nice axis layout
    ax.spines["top"].set_visible(False)
    ax.spines["right"].set_visible(False)
    ax.get_xaxis().tick_bottom()
    ax.get_yaxis().tick_left()
    ax.spines["left"].set_position(("outward", 10))
    ax.spines["bottom"].set_position(("outward", 10))

    # Histogram for axis X1 (feature 5)
    hist_X1.set_ylim(ax.get_ylim())
    hist_X1.hist(
        X[:, 1], bins=hist_nbins, orientation="horizontal", color="grey", ec="grey"
    )
    hist_X1.axis("off")

    # Histogram for axis X0 (feature 0)
    hist_X0.set_xlim(ax.get_xlim())
    hist_X0.hist(
        X[:, 0], bins=hist_nbins, orientation="vertical", color="grey", ec="grey"
    )
    hist_X0.axis("off")

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

def make_plot(item_idx):
    title, X = distributions[item_idx]
    ax_zoom_out, ax_zoom_in, ax_colorbar = create_axes(title)
    axarr = (ax_zoom_out, ax_zoom_in)
    plot_distribution(
        axarr[0],
        X,
        y,
        hist_nbins=200,
        x0_label=feature_mapping[features[0]],
        x1_label=feature_mapping[features[1]],
        title="Full data",
    )

    # zoom-in
    zoom_in_percentile_range = (0, 99)
    cutoffs_X0 = np.percentile(X[:, 0], zoom_in_percentile_range)
    cutoffs_X1 = np.percentile(X[:, 1], zoom_in_percentile_range)

    non_outliers_mask = np.all(X > [cutoffs_X0[0], cutoffs_X1[0]], axis=1) & np.all(
        X < [cutoffs_X0[1], cutoffs_X1[1]], axis=1
    )
    plot_distribution(
        axarr[1],
        X[non_outliers_mask],
        y[non_outliers_mask],
        hist_nbins=50,
        x0_label=feature_mapping[features[0]],
        x1_label=feature_mapping[features[1]],
        title="Zoom-in",
    )

    norm = mpl.colors.Normalize(y_full.min(), y_full.max())
    mpl.colorbar.ColorbarBase(
        ax_colorbar,
        cmap=cmap,
        norm=norm,
        orientation="vertical",
        label="Color mapping for values of y",
    )

Исходные данные

Для каждого преобразования показаны два преобразованных признака: на левом графике представлен весь набор данных, а на правом — увеличенный фрагмент набора данных без маргинальных выбросов. Подавляющее большинство образцов сосредоточено в определённом диапазоне: [0, 10] для медианного дохода и [0, 6] для средней заполняемости домов. Обратите внимание, что присутствуют некоторые маргинальные выбросы (средняя заполняемость некоторых районов превышает 1200). Поэтому выбор определённого метода предобработки может быть очень полезен в зависимости от задачи. Далее мы рассмотрим особенности и поведение этих методов предобработки при наличии маргинальных выбросов.

make_plot(0)
Unscaled data, Full data, Zoom-in

StandardScaler

StandardScaler вычитает среднее значение и масштабирует данные до единичной дисперсии. Как показано на левом графике ниже, масштабирование сужает диапазон значений признака. Однако выбросы влияют на вычисление выборочного среднего и стандартного отклонения. В частности, поскольку выбросы каждого признака имеют разный масштаб, разброс преобразованных данных по каждому признаку сильно различается: большая часть данных находится в диапазоне [-2, 4] для преобразованного признака медианного дохода, тогда как те же данные сжаты в более узком диапазоне [-0.2, 0.2] для преобразованного признака средней заполняемости домов.

Таким образом, StandardScaler не гарантирует сбалансированные масштабы признаков при наличии выбросов.

make_plot(1)
Data after standard scaling, Full data, Zoom-in

MinMaxScaler

MinMaxScaler масштабирует набор данных так, чтобы все значения признаков попадали в диапазон [0, 1], как показано на правом графике ниже. Однако при таком масштабировании все внутренние значения преобразованного признака средней заполняемости домов сжимаются в узкий диапазон [0, 0.005].

И StandardScaler, и MinMaxScaler очень чувствительны к наличию выбросов.

make_plot(2)
Data after min-max scaling, Full data, Zoom-in

MaxAbsScaler

MaxAbsScaler похож на MinMaxScaler, но значения могут отображаться в разных диапазонах в зависимости от того, присутствуют ли отрицательные ИЛИ положительные значения. Если присутствуют только положительные значения, диапазон равен [0, 1]. Если присутствуют только отрицательные значения, диапазон равен [-1, 0]. Если присутствуют и отрицательные, и положительные значения, диапазон равен [-1, 1]. Для данных, содержащих только положительные значения, MinMaxScaler и MaxAbsScaler ведут себя одинаково. Поэтому MaxAbsScaler также чувствителен к наличию крупных выбросов.

make_plot(3)
Data after max-abs scaling, Full data, Zoom-in

RobustScaler

В отличие от предыдущих методов масштабирования, для вычисления статистик центрирования и масштабирования в RobustScaler используются процентили, поэтому на них не влияет небольшое количество очень крупных маргинальных выбросов. В результате диапазон преобразованных значений признаков оказывается шире, чем при использовании предыдущих методов, и, что важнее, приблизительно одинаковым: для обоих признаков большинство преобразованных значений находится в диапазоне [-2, 3], как видно на увеличенном графике. Обратите внимание, что сами выбросы остаются в преобразованных данных. Если требуется отдельно отсечь выбросы, необходимо нелинейное преобразование (см. ниже).

make_plot(4)
Data after robust scaling, Full data, Zoom-in

PowerTransformer

PowerTransformer применяет степенное преобразование к каждому признаку, чтобы сделать распределение данных более похожим на нормальное, стабилизировать дисперсию и уменьшить асимметрию. Сейчас поддерживаются преобразования Йео—Джонсона и Бокса—Кокса, а оптимальный коэффициент масштабирования в обоих случаях определяется методом максимального правдоподобия. По умолчанию PowerTransformer нормализует данные до нулевого среднего и единичной дисперсии. Обратите внимание, что преобразование Бокса—Кокса применимо только к строго положительным данным. Доход и средняя заполняемость домов в данном случае строго положительны, однако при наличии отрицательных значений предпочтительно преобразование Йео—Джонсона.

make_plot(5)
make_plot(6)
  • Data after power transformation (Yeo-Johnson), Full data, Zoom-in
  • Data after power transformation (Box-Cox), Full data, Zoom-in

QuantileTransformer (равномерное распределение на выходе)

QuantileTransformer выполняет нелинейное преобразование, отображая функцию плотности вероятности каждого признака на равномерное или гауссово распределение. В этом случае все данные, включая выбросы, будут отображены на равномерное распределение в диапазоне [0, 1], поэтому выбросы станут неотличимы от остальных наблюдений.

RobustScaler и QuantileTransformer устойчивы к выбросам в том смысле, что добавление или удаление выбросов из обучающего набора приводит приблизительно к одному и тому же преобразованию. Однако, в отличие от RobustScaler, QuantileTransformer автоматически ограничивает значения любых выбросов заранее заданными границами диапазона (0 и 1). Это может привести к эффектам насыщения для экстремальных значений.

make_plot(7)
Data after quantile transformation (uniform pdf), Full data, Zoom-in

QuantileTransformer (гауссово распределение на выходе)

Чтобы преобразовать данные в гауссово распределение, задайте параметр output_distribution='normal'.

make_plot(8)
Data after quantile transformation (gaussian pdf), Full data, Zoom-in

Normalizer

Normalizer масштабирует вектор каждого образца до единичной нормы независимо от распределения образцов. На обоих графиках ниже видно, что все образцы отображаются на единичную окружность. В нашем примере значения двух выбранных признаков только положительные, поэтому преобразованные данные находятся только в положительном квадранте. Это было бы не так, если бы некоторые исходные признаки содержали как положительные, так и отрицательные значения.

make_plot(9)

plt.show()
Data after sample-wise L2 normalizing, Full data, Zoom-in

Общее время выполнения скрипта: (0 минут 8.119 секунд)

Launch binder
Launch JupyterLite

Download Jupyter notebook: plot_all_scaling.ipynb

Download Python source code: plot_all_scaling.py

Download zipped: plot_all_scaling.zip

Связанные примеры

Преобразование данных в нормальное распределение

Оценка оценщиков для обнаружения выбросов

Регрессия Тейла—Сена

Пример использования IsolationForest

© 2007–2026 The scikit-learn developers
Licensed under the 3-clause BSD License.
https://scikit-learn.org/1.9/auto_examples/preprocessing/plot_all_scaling.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API