Vergleich der Auswirkungen verschiedener Skalierer auf Daten mit Ausreißern#

Feature 0 (Median-Einkommen in einem Block) und Feature 5 (durchschnittliche Hausbelegung) des California Housing Datensatzes haben sehr unterschiedliche Skalen und enthalten einige sehr große Ausreißer. Diese beiden Merkmale führen zu Schwierigkeiten bei der Visualisierung der Daten und, was noch wichtiger ist, sie können die Vorhersageleistung vieler Machine-Learning-Algorithmen verschlechtern. Unskalierte Daten können auch die Konvergenz vieler gradientenbasierter Schätzer verlangsamen oder sogar verhindern.

Tatsächlich sind viele Schätzer so konzipiert, dass sie davon ausgehen, dass jedes Merkmal Werte nahe Null annimmt oder, was noch wichtiger ist, dass alle Merkmale auf vergleichbaren Skalen variieren. Insbesondere metrikbasierte und gradientenbasierte Schätzer gehen oft von ungefähr standardisierten Daten aus (zentrierte Merkmale mit Einheitsvarianzen). Eine bemerkenswerte Ausnahme sind entscheidungsbaum-basierte Schätzer, die robust gegenüber beliebigen Skalierungen der Daten sind.

Dieses Beispiel verwendet verschiedene Skalierer, Transformatoren und Normalisierer, um die Daten in einen vordefinierten Bereich zu bringen.

Skalierer sind lineare (oder genauer affine) Transformatoren und unterscheiden sich voneinander in der Art und Weise, wie sie die Parameter schätzen, die zum Verschieben und Skalieren jedes Merkmals verwendet werden.

QuantileTransformer bietet nichtlineare Transformationen, bei denen Abstände zwischen marginalen Ausreißern und Inlinern verkleinert werden. PowerTransformer bietet nichtlineare Transformationen, bei denen Daten auf eine Normalverteilung abgebildet werden, um die Varianz zu stabilisieren und die Schiefe zu minimieren.

Im Gegensatz zu den vorherigen Transformationen bezieht sich die Normalisierung auf eine pro Stichprobe durchgeführte Transformation und nicht auf eine pro Merkmal durchgeführte Transformation.

Der folgende Code ist etwas ausführlich, springen Sie gerne direkt zur Analyse der Ergebnisse.

# 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")

Für jeden Skalierer/Normalisierer/Transformator werden zwei Diagramme angezeigt. Die linke Abbildung zeigt ein Streudiagramm des gesamten Datensatzes, während die rechte Abbildung die extremen Werte ausschließt und nur 99 % des Datensatzes berücksichtigt, wobei die marginalen Ausreißer ausgeschlossen werden. Zusätzlich werden die marginalen Verteilungen für jedes Merkmal an den Seiten des Streudiagramms angezeigt.

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",
    )

Ursprüngliche Daten#

Jede Transformation wird gezeigt, indem zwei transformierte Merkmale dargestellt werden. Die linke Abbildung zeigt den gesamten Datensatz, die rechte Abbildung ist herangezoomt, um den Datensatz ohne die marginalen Ausreißer zu zeigen. Eine große Mehrheit der Stichproben ist auf einen bestimmten Bereich komprimiert, [0, 10] für das Median-Einkommen und [0, 6] für die durchschnittliche Hausbelegung. Beachten Sie, dass es einige marginale Ausreißer gibt (einige Blöcke haben eine durchschnittliche Belegung von mehr als 1200). Daher kann eine spezifische Vorverarbeitung je nach Anwendung sehr vorteilhaft sein. Im Folgenden präsentieren wir einige Einblicke und Verhaltensweisen dieser Vorverarbeitungsmethoden im Beisein von marginalen Ausreißern.

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

StandardScaler#

StandardScaler entfernt den Mittelwert und skaliert die Daten auf Einheitsvarianz. Die Skalierung verkleinert den Bereich der Merkmalswerte, wie in der folgenden linken Abbildung gezeigt. Die Ausreißer haben jedoch einen Einfluss auf die Berechnung des empirischen Mittelwerts und der Standardabweichung. Beachten Sie insbesondere, dass die Streuung der transformierten Daten für jedes Merkmal sehr unterschiedlich ist, da die Ausreißer bei jedem Merkmal unterschiedliche Größenordnungen haben: Die meisten Daten liegen im Bereich [-2, 4] für das transformierte Median-Einkommensmerkmal, während die gleichen Daten im kleineren Bereich [-0.2, 0.2] für die transformierte durchschnittliche Hausbelegung komprimiert werden.

StandardScaler kann daher bei Vorhandensein von Ausreißern keine ausgewogenen Merkmalskalen garantieren.

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

MinMaxScaler#

MinMaxScaler skaliert den Datensatz so, dass alle Merkmalswerte im Bereich [0, 1] liegen, wie im folgenden rechten Bereich gezeigt. Diese Skalierung komprimiert jedoch alle Inliner in den engen Bereich [0, 0.005] für die transformierte durchschnittliche Hausbelegung.

Sowohl StandardScaler als auch MinMaxScaler sind sehr empfindlich gegenüber der Anwesenheit von Ausreißern.

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

MaxAbsScaler#

MaxAbsScaler ähnelt MinMaxScaler, außer dass die Werte je nachdem, ob negative ODER positive Werte vorhanden sind, über mehrere Bereiche abgebildet werden. Wenn nur positive Werte vorhanden sind, ist der Bereich [0, 1]. Wenn nur negative Werte vorhanden sind, ist der Bereich [-1, 0]. Wenn sowohl negative als auch positive Werte vorhanden sind, ist der Bereich [-1, 1]. Bei rein positiven Daten verhalten sich sowohl MinMaxScaler als auch MaxAbsScaler ähnlich. MaxAbsScaler leidet daher ebenfalls unter dem Vorhandensein großer Ausreißer.

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

RobustScaler#

Im Gegensatz zu den bisherigen Skalierern basieren die Zentrierungs- und Skalierungsstatistiken von RobustScaler auf Perzentilen und werden daher nicht von einer kleinen Anzahl sehr großer marginaler Ausreißer beeinflusst. Folglich ist der resultierende Bereich der transformierten Merkmalswerte größer als bei den bisherigen Skalierern und, was noch wichtiger ist, ungefähr ähnlich: Für beide Merkmale liegen die meisten transformierten Werte in einem Bereich von [-2, 3], wie in der herangezoomten Abbildung zu sehen ist. Beachten Sie, dass die Ausreißer selbst in den transformierten Daten noch vorhanden sind. Wenn ein separates Clipping von Ausreißern gewünscht ist, ist eine nichtlineare Transformation erforderlich (siehe unten).

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

PowerTransformer#

PowerTransformer wendet eine Potenztransformation auf jedes Merkmal an, um die Daten eher Gauß-artig zu machen, um die Varianz zu stabilisieren und die Schiefe zu minimieren. Derzeit werden die Yeo-Johnson- und Box-Cox-Transformationen unterstützt, und der optimale Skalierungsfaktor wird bei beiden Methoden über die Maximum-Likelihood-Schätzung bestimmt. Standardmäßig wendet PowerTransformer eine Nullmittelwert-, Einheitsvarianz-Normalisierung an. Beachten Sie, dass Box-Cox nur auf rein positive Daten angewendet werden kann. Einkommen und durchschnittliche Hausbelegung sind zufällig rein positiv, aber wenn negative Werte vorhanden sind, ist die Yeo-Johnson-Transformation vorzuziehen.

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 (gleichmäßige Ausgabe)#

QuantileTransformer wendet eine nichtlineare Transformation an, so dass die Wahrscheinlichkeitsdichtefunktion jedes Merkmals auf eine Gleichmäßigkeits- oder Gauß-Verteilung abgebildet wird. In diesem Fall werden alle Daten, einschließlich der Ausreißer, auf eine Gleichmäßigkeitsverteilung mit dem Bereich [0, 1] abgebildet, wodurch Ausreißer von Inlinern nicht unterscheidbar werden.

RobustScaler und QuantileTransformer sind robust gegenüber Ausreißern in dem Sinne, dass das Hinzufügen oder Entfernen von Ausreißern im Trainingsdatensatz ungefähr die gleiche Transformation ergibt. Aber im Gegensatz zu RobustScaler wird QuantileTransformer auch automatisch Ausreißer kollabieren, indem sie auf die vordefinierten Bereichsgrenzen (0 und 1) gesetzt werden. Dies kann zu Sättigungsartefakten bei extremen Werten führen.

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

QuantileTransformer (Gauß-Ausgabe)#

Um auf eine Gauß-Verteilung abzubilden, setzen Sie den Parameter output_distribution='normal'.

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

Normalizer#

Der Normalizer skaliert den Vektor jeder Stichprobe so, dass er unabhängig von der Verteilung der Stichproben eine Einheitsnorm hat. Dies ist in beiden untenstehenden Abbildungen zu sehen, wo alle Stichproben auf den Einheitskreis abgebildet werden. In unserem Beispiel haben die beiden ausgewählten Merkmale nur positive Werte; daher liegen die transformierten Daten nur im positiven Quadranten. Dies wäre nicht der Fall, wenn einige ursprüngliche Merkmale eine Mischung aus positiven und negativen Werten aufwiesen.

make_plot(9)

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

Gesamtlaufzeit des Skripts: (0 Minuten 7,123 Sekunden)

Verwandte Beispiele

Daten auf eine Normalverteilung abbilden

Daten auf eine Normalverteilung abbilden

Bewertung von Ausreißererkennungs-Schätzern

Bewertung von Ausreißererkennungs-Schätzern

Theil-Sen Regression

Theil-Sen Regression

IsolationForest Beispiel

IsolationForest Beispiel

Galerie generiert von Sphinx-Gallery