Target Encodingについて質問

すみません、文字数の制限なのかどうしても該当のフォーラムのコメント欄に書き込めなかったので、わざわざ新しくトピックを開いて質問させていただきます。ご迷惑ご容赦ください。

Target Encodingを初めて使ってみようと、以下のフォーラムの実装を参考にコードを書いたのですが、この例だと各行のTarget Encodingをかけた列が全て同じ値になってしまい、特徴量が消えてしまっている気がするのですが、これは実装としてうまくいっているのでしょうか?
https://prob.space/competitions/game_winner/discussions/Oregin-Post344b005eb605a8777141

import pandas as pd
import numpy as np
import re
import matplotlib.pyplot as plt
import seaborn as sns
import lightgbm as lgb
from sklearn.model_selection import train_test_split
from sklearn.model_selection import KFold
from sklearn.metrics import accuracy_score
import warnings
warnings.filterwarnings('ignore')

# データの読込
train = pd.read_csv("./features/train_data.csv")
test  = pd.read_csv('./features/test_data.csv')

# 欠損値は、全て「-1」とする。
def fill_all_null(df):
    for col_name in df.columns[df.isnull().sum()!=0]:
        df[col_name] = df[col_name].fillna(-1)

# 訓練データ、テストデータの欠損値を補完
fill_all_null(train)
fill_all_null(test)

# ターゲットエンコーディングの関数定義
def change_to_target2(train_df,test_df,input_column_name,output_column_name):
    from sklearn.model_selection import KFold
    
    # nan埋め処理
    train[input_column_name] = train[input_column_name].fillna('-1').isnull().sum()
    test[input_column_name] = test[input_column_name].fillna('-1').isnull().sum()

    kf = KFold(n_splits=5, shuffle=True, random_state=71)
    #=========================================================#
    c=input_column_name
    # 学習データ全体で各カテゴリにおけるyの平均を計算
    data_tmp = pd.DataFrame({c: train_df[c],'target':train_df['y']})
    target_mean = data_tmp.groupby(c)['target'].mean()
    #テストデータのカテゴリを置換
    test_df[output_column_name] = test_df[c].map(target_mean)
    
    # 変換後の値を格納する配列を準備
    tmp = np.repeat(np.nan, train_df.shape[0])

    for i, (train_index, test_index) in enumerate(kf.split(train_df)): # NFOLDS回まわる
        #学習データについて、各カテゴリにおける目的変数の平均を計算
        target_mean = data_tmp.iloc[train_index].groupby(c)['target'].mean()
        #バリデーションデータについて、変換後の値を一時配列に格納
        tmp[test_index] = train_df[c].iloc[test_index].map(target_mean) 

    #変換後のデータで元の変数を置換
    train_df[output_column_name] = tmp
#========================================================#

# オブジェクトの列のリストを作成
object_col_list = train.select_dtypes(include=object).columns

# オブジェクトの列は全てターゲットエンコーディング実施
for col in object_col_list:
    change_to_target2(train,test,col,"enc_"+col)

# 変換前の列を削除
train = train.drop(object_col_list,axis=1)
test = test.drop(object_col_list,axis=1)

# 'id'の列を削除
train = train.drop('id',axis=1)
test  = test.drop('id',axis=1)

enc_obj_list = ['enc_'+s for s in object_col_list.tolist()]
print(train[enc_obj_list].head())
enc_period  enc_game-ver  enc_lobby-mode  enc_lobby  enc_mode  enc_stage  \
0    0.525161      0.525161        0.525161   0.525161  0.525161   0.525161   
1    0.524499      0.524499        0.524499   0.524499  0.524499   0.524499   
2    0.525747      0.525747        0.525747   0.525747  0.525747   0.525747   
3    0.523781      0.523781        0.523781   0.523781  0.523781   0.523781   
4    0.524329      0.524329        0.524329   0.524329  0.524329   0.524329   

   enc_A1-weapon  enc_A1-rank  enc_A2-weapon  enc_A2-rank     ...       \
0       0.525161     0.525161       0.525161     0.525161     ...        
1       0.524499     0.524499       0.524499     0.524499     ...        
2       0.525747     0.525747       0.525747     0.525747     ...        
3       0.523781     0.523781       0.523781     0.523781     ...        
4       0.524329     0.524329       0.524329     0.524329     ...        

   enc_A4-weapon  enc_A4-rank  enc_B1-weapon  enc_B1-rank  enc_B2-weapon  \
0       0.525161     0.525161       0.525161     0.525161       0.525161   
1       0.524499     0.524499       0.524499     0.524499       0.524499   
2       0.525747     0.525747       0.525747     0.525747       0.525747   
3       0.523781     0.523781       0.523781     0.523781       0.523781   
4       0.524329     0.524329       0.524329     0.524329       0.524329   

   enc_B2-rank  enc_B3-weapon  enc_B3-rank  enc_B4-weapon  enc_B4-rank  
0     0.525161       0.525161     0.525161       0.525161     0.525161  
1     0.524499       0.524499     0.524499       0.524499     0.524499  
2     0.525747       0.525747     0.525747       0.525747     0.525747  
3     0.523781       0.523781     0.523781       0.523781     0.523781  
4     0.524329       0.524329     0.524329       0.524329     0.524329  

[5 rows x 22 columns]

添付データ

  • test.ipynb?X-Amz-Expires=10800&X-Amz-Date=20240420T111644Z&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIP7GCBGMWPMZ42PQ
  • Aws4 request&x amz signedheaders=host&x amz signature=fda99b606b130657494f1a01406be7907adda7fc99014949724ec0d41899a147
    cha_kabu

    関数の途中にある、「# nan埋め処理」がプログラムミスと思われます。
    元のカテゴリ変数の値を、target encodingの前にすべて0に置き換えてしまっています。
    ※「# 変換前の列を削除」の処理を実行せずに関数を適用したデータフレームを確認すると分かるかと思います。

    関数外で「# 欠損値は、全て「-1」とする。」処理をしているので、「# nan埋め処理」のプログラムは削除して良いかと思います。

    Aws4 request&x amz signedheaders=host&x amz signature=965238a613a11b5aa13dd6a5ee092fc8c1911b0de55216074f6cbb426bd35c98
    pop-ketle

    確かに「# nan埋め処理」の部分がまずそうです。
    ループのたびに、欠損値を-1で埋めるようにみせかけつつ、欠損値の数をカウントしてその合計をinput_column_nameに入れるというよくわからない処理になっています。すでに欠損値を-1で埋めているので、input_column_nameの列がすべて欠損値の合計値0に入れ替わっています。

    そのため、結果的に毎度全ての値が0の列に対してtarget encodingを行う結果となり、各行の各列が同じ値になっていて、行ごとに値が違うのは、バリデーションを切ってるので、どの時のバリデーションデータに入るかで値が変わっているようです。

    ありがとうございます。プログラムのミスの箇所がわかり、処理している内容についての理解も深まりました。

    Favicon
    new user
    コメントするには 新規登録 もしくは ログイン が必要です。