HOWTO · Pandas

Pandas で数値 DataFrame 列を正規化する 3 つの方法

数値の Pandas DataFrame 列に対する平均(z スコア)標準化、min-max スケーリング、分位点正規化を、数式と再利用可能な例で比較します。

このページの内容

Pandas DataFrame の数値列を再スケーリングするには、3つの方法から選べます。平均による正規化(zスコア標準化)は各列を中心化し、標準偏差でスケーリングします。min-maxスケーリングは定数でない各列を0~1の範囲に変換し、分位点正規化は順位に基づいて値を共通の分布に近づけます。適切な方法は、モデルが必要とするスケール、外れ値への感度、各列の分布をそろえる必要があるかどうかによって異なります。

Pandasでのデータ正規化

正規化は値を再スケーリングする処理で、標準化は平均を基準に値を中心化し、標準偏差単位で表す処理です。このガイドでは、列ごとに適用する3つの変換方法を紹介します。Pandasにはこれらの式を一度に適用する専用のDataFrameメソッドはありませんが、算術演算や順位付けを使えば、それぞれ簡単に実装できます。

各例では、4つの数値列を持つ同じ小規模で決定的なDataFrameを使います。変換は列ごとに行われるため、列名と行インデックスは保持されます。

import pandas as pd


df = pd.DataFrame({
    "A": [1, 2, 3, 4],
    "B": [10, 20, 30, 40],
    "C": [4, 1, 3, 2],
    "D": [5, 5, 5, 5],
})

print(df)
   A   B  C  D
0  1  10  4  5
1  2  20  1  5
2  3  30  3  5
3  4  40  2  5

D列は定数列で、min-maxの節ではその動作を明確に確認できます。以下の例はそれぞれ単独で実行できるよう、同じ入力データを作り直します。

meanによるPandas DataFrameの正規化

平均を使う式は(x - mean) / standard deviationで、列ごとに計算します。これはzスコア標準化です。変換後の各列の平均はほぼ0になり、値は標準偏差単位で表されますが、0~1の範囲には制限されません。PandasのSeries.std()は既定で標本標準偏差(ddof=1)を使います。母標準偏差を使う計算ではddof=0を指定してください。

import pandas as pd


df = pd.DataFrame({
    "A": [1, 2, 3, 4],
    "B": [10, 20, 30, 40],
    "C": [4, 1, 3, 2],
    "D": [5, 5, 5, 5],
})

mean_norm = df.apply(lambda column: (column - column.mean()) / column.std(), axis=0)
print(mean_norm.round(3))
       A      B      C   D
0 -1.162 -1.162  1.162 NaN
1 -0.387 -0.387 -1.162 NaN
2  0.387  0.387  0.387 NaN
3  1.162  1.162 -0.387 NaN

この変換は、値を中心化し、比較しやすいスケールにする方法が役立つ場合に適しています。定数列では標準偏差が0になるため、この式の結果は欠損値になります。その特徴量を削除するか、用途に応じて別途処理するかを決めてください。

min-maxによるPandas DataFrameの正規化

列ごとのmin-maxスケーリングには(x - min) / (max - min)を使います。定数でない各列では最小値が0、最大値が1になり、ほかの値はその間に比例して配置されます。以下のヘルパー関数は一貫して引数のdf_inputを使い、変化のない特徴量に対する一般的で中立的な選択として、定数列を0に変換します。

import pandas as pd


def minmax_norm(df_input):
    column_range = df_input.max() - df_input.min()
    scaled = (df_input - df_input.min()) / column_range
    return scaled.fillna(0)


df = pd.DataFrame({
    "A": [1, 2, 3, 4],
    "B": [10, 20, 30, 40],
    "C": [4, 1, 3, 2],
    "D": [5, 5, 5, 5],
})

print(minmax_norm(df).round(3))
       A      B      C    D
0  0.000  0.000  1.000  0.0
1  0.333  0.333  0.000  0.0
2  0.667  0.667  0.667  0.0
3  1.000  1.000  0.333  0.0

一定の範囲に収めたい場合、min-maxスケーリングが適しています。観測された最小値と最大値でスケールが決まるため、極端な値があると、同じ列のほかの値が狭い範囲に押し込まれることがあります。ヘルパー関数の最後にあるfillna(0)は、数値の定数列を0に変換しますが、入力にもともと含まれていた欠損値もすべて0に置き換えます。平均値で補完したい場合は、たとえばminmax_norm(df_input.fillna(df_input.mean()))のように、関数を呼び出す前に欠損値を補完してください。データに合った補完方針を選びましょう。

quantileによるPandas DataFrameの正規化

分位点正規化は、各列が同じ基礎分布に従うと想定できる場合に役立ちます。各列をソートし、各順位の値を列間で平均した後、その順位ごとの平均を元の行に対応させます。他の2つの方法とは異なり、列の経験分布をそろえるように値を変換するため、各列が共通の分布を持つという前提が妥当な場合にだけ使ってください。

この実装ではrank(method="min")を使います。同じ値には、その同順位グループで最も小さい順位が割り当てられます。同順位の値の順位を平均するわけではないため、同順位があると変換後の分布が異なることがあります。共通の例では、D列は定数です。4つの値はすべて最小順位で同順位になるため、共通の順位値に広がるのではなく、同じ順位平均に対応します。

import numpy as np
import pandas as pd


def quantile_norm(df_input):
    sorted_values = np.sort(df_input.to_numpy(), axis=0)
    rank_means = sorted_values.mean(axis=1)
    rank_to_mean = pd.Series(rank_means, index=np.arange(1, len(rank_means) + 1))
    ranks = df_input.rank(method="min").stack().astype(int)
    return ranks.map(rank_to_mean).unstack()


df = pd.DataFrame({
    "A": [1, 2, 3, 4],
    "B": [10, 20, 30, 40],
    "C": [4, 1, 3, 2],
    "D": [5, 5, 5, 5],
})

print(quantile_norm(df).round(3))
       A      B      C     D
0   4.25   4.25  13.25  4.25
1   7.25   7.25   4.25  4.25
2  10.25  10.25  10.25  4.25
3  13.25  13.25   7.25  4.25

定数のD列は、共通分布の前提が重要な理由を示しています。繰り返し出現する値はすべて最小順位になり、4.25に対応するため、ほかの列の経験分布とは一致しません。一般的な数値特徴量では、目的に合わせて方法を選びましょう。中心化と標準偏差によるスケーリングにはzスコア、外れ値への注意を前提に一定範囲へ収めるにはmin-max、データから列の分布をそろえる妥当性が確認できる場合には分位点正規化を選びます。