search
LoginSignup
1

posted at

updated at

[kaggle / python] 回帰問題(house prices)の超初歩(4)~数値変数のカテゴリ変数化~

前回の続きです。
前回の記事はこちら。

Summary of the previous article(前回のあらまし)

前回は、kaggle の House Prices の課題に対して、簡単にできる「新しい特徴量の作成と、不要と思われる特徴量の削除」を行いました。

結果としてScoreが、0.15160から0.15140へと微改善しました。
(微改善ってなんだ)

Today's result

そして今回は、数値で表現されている特徴量(カラム)に対する操作を行います。
具体的には、一部の数値データ特徴量を、カテゴリ変数(文字列)化します。

やったこと score
前回 - 0.15140
今回 一部数値特徴量のカテゴリ化 0.14987

Why?

最初この操作を本やネット上で見かけたとき、なぜ数値を文字列化するのか、なぜこれが効果があるのかわかっていませんでしたが、自分なりに考えた意味合いを記載しておきます。

なぜ数値データをわざわざ文字列データに変換するかというと、数値データにもかかわらず、

  • 数値の大小関係数値間の間隔に特段意味がない

場合があるからではないでしょうか。

こういった数値データを文字列化することによって、そのカラムにおける数値の大小関係や、数値ごとの間隔幅といった、意味をなさない情報をモデルから除去することができます。

さて、実際にやったことを見ていきます(非常に簡単です)

Part.1

1. source cord

1-0. same as the previous cord

またまた、前回と同じソースをこの「1-0.」にまとめておきます(^ワ^*)
今回ここで、やっていることは、

  1. モジュール読み込み
  2. データ読み込み
  3. 特徴量の追加と削除

です。

# 1. import modules
import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
from xgboost import XGBClassifier, XGBRegressor

# 2. load data
import os
for dirname, _, filenames in os.walk('/kaggle/input'):
    for filename in filenames:
        print(os.path.join(dirname, filename))
train = pd.read_csv('/kaggle/input/house-prices-advanced-regression-techniques/train.csv')
test = pd.read_csv('/kaggle/input/house-prices-advanced-regression-techniques/test.csv')

# 3-1. add new column
train['totalRoomsSF'] = train['1stFlrSF'] + train['2ndFlrSF'] + train['TotalBsmtSF']
test['totalRoomsSF'] = test['1stFlrSF'] + test['2ndFlrSF'] + test['TotalBsmtSF']

# 3-2. drop ID column
train_ID = train['Id']
test_ID = test['Id']
train = train.drop(['Id'], axis=1)
test = test.drop(['Id'], axis=1)

1-1. cast some integer columns

ここで今回の変更点の一つ目ですが、3つのカラムが数値としての大小や間隔にさほど意味がないようなので、これらを文字列化します。

ターゲットは、MSSubClassYrSoldMoSoldです。

# 4. cast columns
train['MSSubClass'] = train['MSSubClass'].apply(str)
train['YrSold'] = train['YrSold'].astype(str)
train['MoSold'] = train['MoSold'].astype(str)
test['MSSubClass'] = test['MSSubClass'].apply(str)
test['YrSold'] = test['YrSold'].astype(str)
test['MoSold'] = test['MoSold'].astype(str)

MSSubClassについては、ここ(kaggle)を見てもらえるとわかりますが、明らかに数値の大小関係や間隔が、その実際の意味合いと関係ありません。

YrSoldMoSoldについては、販売された年と月です。
これが数値として意味がないかといわれると、個人的には迷いました。

ですが、落ち着いて考えてみると、1月や2月と比べて12月の方が何か物理的に大きい(もしくは小さい)といったことはないですし、1月と3月の差が、5月と7月の差と同程度になるような実質的な意味合いもなさそうなので、やはりこれもカテゴリ変数化してよさそうです。

1-2. same as the previous cord

続いて、前回と同じコードになっているところはこの1-2にまとめておきます。

5.説明変数と目的変数の分割
6.学習データとテストデータの分割

# 5. split data into explanatory variable(説明変数) and response variable(目的変数)
tmp_train_x = train.drop('SalePrice', axis=1)
tmp_train_y = train['SalePrice']

# 6. split data into training data and test data
x_train, x_test, y_train, y_test = \
    train_test_split(tmp_train_x, tmp_train_y, test_size=0.20, random_state=0)

1-3. add the target column for Label Encoding

今回、Label Encodingの対象となるカラムが増えます。
なぜかというと、、、文字列はXGBoostに投入できないから....

あれ、せっかく数値を文字列に変換したのに、また数値に戻すの?
っていう話ですが、、、まあそこは試しにそのままやってみましょう!!

# 7. LabelEncoding
from sklearn.preprocessing import LabelEncoder

x_train_label_encoded = x_train.copy()
x_test_label_encoded = x_test.copy()

# ※注意
# NOTE: trainingデータとtestデータ両方を同じルールでEncodingするために、
#   結合した状態でEncodingにかける
df_all_data = pd.concat([x_train, x_test, test])
not_expected_type_column_names = train.columns[train.dtypes == 'object']

for col_name in encode_target_columns:
    target_all_data_column = df_all_data[col_name].fillna('NaN')
    le = LabelEncoder()
    le.fit(target_all_data_column)

    target_train_column = x_train[col_name].fillna('NaN')
    target_test_column = x_test[col_name].fillna('NaN')
    x_train_label_encoded[col_name] = le.transform(target_train_column)
    x_test_label_encoded[col_name] = le.transform(target_test_column)

1-4. same as the previous cord

ここは前回と一緒です

8.学習model作成
9.価格予測

# 8. create model(学習)
model = XGBRegressor(n_estimators=20, random_state=0)
model.fit(x_train_label_encoded, y_train)

# 9. prediction(推論)
predict_result_for_tr_data = model.predict(x_train_label_encoded)
predict_result = model.predict(x_test_label_encoded)
# 学習データをpredictした結果
rmse_of_train = np.sqrt(mean_squared_error(predict_result_for_tr_data, y_train))
rmse_of_train

8181.078833664238 (前回: 7681.997640750206) # 点数悪化してるが....
# テストデータをpredictした結果
rmse_of_test = np.sqrt(mean_squared_error(predict_result, y_test))
rmse_of_test

35119.73177043754 (前回: 30411.796986629754) # ひどく悪化してる...大丈夫か?

1-5. Encode test data for submission

最後ですが、提出用用データの特徴量についても、今回カテゴリ変数化したデータをLabel Encodingしてあげる必要があるので、ここの処理はほんの少し変わります。。

# 10. create csv for submission
test_encoded = test.copy()

# 提出用データについても、今回変換対象としたcolumn(encode_target_columns)をencode
for col_name in encode_target_columns:
    target_all_data_column = df_all_data[col_name].fillna('NaN')
    le = LabelEncoder()
    le.fit(target_all_data_column)

    target_test_column = test[col_name].fillna('NaN')
    test_encoded[col_name] = le.transform(target_test_column)

predict_submission = model.predict(test_encoded)
submission = pd.DataFrame(
    {'Id': test['Id'], 'SalePrice': predict_submission}
)
submission.to_csv('submission_new.csv', index=False)

この結果、スコアは0.15140から0.14987へと改善されました!
学習データや検証用データでの成績(RSME)は悪化しているので、これくらいは誤差なのだろうか...

Next step

次は、Label EncodingしているところをOne Hot Encodingに置き換えてみようと思う。

今回の(4)では、せっかくカテゴリ変数化したカラムを、わざわざLabel Encodingで再度数値に戻してしまっていた。
このせいで、おそらく若干ではあるが、数値の大小・順序関係がXGBoostによって考慮されてしまい、結果として何らかの悪影響が出ている可能性がある。

数値の大小・順序関係が残っていることによる影響を完全に除去することで、成績が改善するかどうかを見たい。
(もし改善した場合は、Label EncodingとXGBoostはもしかしたら相性が悪いと言えるかもしれない)

そのさらに次(6)は、外れ値の除去に挑戦してみたいと思っているのですが、気が変わるかもしれません₍₍ (ง ˘ω˘ )ว ⁾⁾スヤッスヤッ

References

シリーズ一覧

No New Trial Score Link Note
1 xgboost 0.15663 こちら
2 Label Encoding 0.15160 こちら
3 Add and delete column 0.15140 こちら
4 Make integer into categorical 0.14987 - 本記事
5 One hot Encoding 0.14835 こちら
6 Hyper parameters tuning 0.13948 こちら
7 Logarithimic transformation 0.13347 記事未作成

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
What you can do with signing up
1