📈

Pandas でグループごとに標準化する

2021/05/26に公開

公式ドキュメントの Example に書いてありました。なんてこったい...


以下のようなグループを持つテーブルデータに対して、グループごとに標準化(もしくは正規化)するのを Pandas でいい感じにできないか調べたので、そのメモです。

グループ 値 1 値 2
A 0 10
A 20 30
B 100 200
B 300 400

結論

Pandas の Groupby と Transform を使えばいい感じにできた!

import pandas as pd
import numpy as np

# 標準化を行う関数の定義
def scale(x):
    res = (x - np.mean(x)) / np.std(x, ddof=1)
    return res
    
df_scale = df.groupby("グループ").transform(scale)

実際にやってみた

ランダムに整数を作り、それを A と B の2つのグループに分けて、グループごとに標準化を実行してみた。

import numpy as np
import pandas as pd

np.random.seed(123)

# 標準化を行う関数の定義
def scale(x):
    res = (x - np.mean(x)) / np.std(x, ddof=1)
    return res
    
# データの準備
X = np.random.randint(0, 100, 12).reshape(6, 2)
groups = ["A", "A", "A", "B", "B", "B"]
df = pd.DataFrame(X, columns=["値 1", "値 2"])
df.insert(0, "グループ", groups)

# 標準化の実施
df_scale = df.groupby("グループ").transform(scale)
df_scale.insert(0, "グループ", groups)

print("標準化する前")
print(df)
print("標準化した後")
print(df_scale)

実行結果

標準化する前
  グループ  値 1  値 2
0    A   66   92
1    A   98   17
2    A   83   57
3    B   86   97
4    B   96   47
5    B   73   32
標準化した後
  グループ       値 1       値 2
0    A -1.020169  0.977054
1    A  0.978530 -1.021466
2    A  0.041640  0.044412
3    B  0.086711  1.126315
4    B  0.953821 -0.342791
5    B -1.040532 -0.783523

グループ A の値 1 に対して R で検算してみると、以下のようになり、結果が一致した。

> scale(c(66, 98, 83))
            [,1]
[1,] -1.02016938
[2,]  0.97852981
[3,]  0.04163957
attr(,"scaled:center")
[1] 82.33333
attr(,"scaled:scale")
[1] 16.01041

結果の上書き

元のテーブルを標準化した値で上書きするには、

# 元のデータを標準化した値で上書きする場合の例
df.loc[:, df_scale.columns] = df_scale

とすれば OK!!

Discussion