LoginSignup
1
2

More than 3 years have passed since last update.

複数変数の正規化処理:独立にfitしてtransformしてinverse_transformする

Posted at

やりたいこと

StandardScalerやMinMaxScalerで正規化処理をするときに、ある変数だけinverse_transformしたいときなどがあります。しかし、n変数まとめてfitしてしまっていると、特定の変数だけを逆変換するわけにはいきません。

非常にナイーブな実装ですが、n変数に対してn個のscaler()を用意して、リストに格納してぶん回します。
これだとあまりに記事にするほどの価値がないので、このリストに入ったScalerをsave/loadして使いまわす方法も紹介しておきます。

やったこと

変換対象の配列を用意します。ここでは3変数とします。

ar1 = np.arange(10)
ar2 = np.arange(10)+10
ar3 = np.arange(10)+20
arrs = np.block([[ar1], [ar2], [ar3]])
arrs = arrs.T

print(arrs)
# array([[ 0, 10, 20],
#        [ 1, 11, 21],
#        [ 2, 12, 22],
#        [ 3, 13, 23],
#        [ 4, 14, 24],
#        [ 5, 15, 25],
#        [ 6, 16, 26],
#        [ 7, 17, 27],
#        [ 8, 18, 28],
#        [ 9, 19, 29]])

Scalerをn個(この例では3つ)用意します。

from sklearn.preprocessing import StandardScaler, MinMaxScaler

n = arrs.shape[1] # 変数の数, ここでは3
scaler_list = [StandardScaler() for i in range(n)]

for文でぶん回しながらfitします

for i in range(3):
    scaler_list[i].fit(arrs[:,i].reshape(-1, 1))

各列についてtransformしていきます。
格納先として乱数の入った配列transを用意しておきます。
ポイントは、2つ。
1. 一次元の配列は(m, 1)次元にしないと受け付けてもらえないのでreshape(-1,1)しているところ
2. それを配列transに格納するときには逆に(m, )というshapeに戻しておかないといけないのでnp.ndarray.squeeze()しているところ

trans= np.random.random(arrs.shape)
for i in range(n):
    trans[:,i] = scaler_list[i].transform(arrs[:,i].reshape(-1, 1)).squeeze()

print(trans)
# array([[-1.5666989 , -1.5666989 , -1.5666989 ],
#        [-1.21854359, -1.21854359, -1.21854359],
#        [-0.87038828, -0.87038828, -0.87038828],
#        [-0.52223297, -0.52223297, -0.52223297],
#        [-0.17407766, -0.17407766, -0.17407766],
#        [ 0.17407766,  0.17407766,  0.17407766],
#        [ 0.52223297,  0.52223297,  0.52223297],
#        [ 0.87038828,  0.87038828,  0.87038828],
#        [ 1.21854359,  1.21854359,  1.21854359],
#        [ 1.5666989 ,  1.5666989 ,  1.5666989 ]])

これをscaler.inverse_transform()で戻します。さっきのコードをちょっとだけいじれば終わりですね。ここでも格納先にinvという配列を用意しておきます。

inv = np.random.random(arrs.shape)
for i in range(n):
    inv[:,i] = scaler_list[i].inverse_transform(res[:,i].reshape(-1, 1)).squeeze()

print(inv)
# array([[ 0, 10, 20],
#        [ 1, 11, 21],
#        [ 2, 12, 22],
#        [ 3, 13, 23],
#        [ 4, 14, 24],
#        [ 5, 15, 25],
#        [ 6, 16, 26],
#        [ 7, 17, 27],
#        [ 8, 18, 28],
#        [ 9, 19, 29]])

無事に元の配列arrsに戻っていることが確認できました。成功です。今回はすべてnumpy.ndarrayに閉じていますが、pandas.DataFrameに格納しておけば必要な時に特定のカラムだけ逆変換して戻すといったことができそうです。そのときはscaler_listの何番目がどのカラムに対応しているのか、という対応表を辞書で持っておくと安全でしょうか。

Scalerでこういうことができるということは、sklearn.preprocessingに入っているメソッド(のオブジェクト)なら同じことができると思います。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2