[kaggle / python] 回帰問題（house prices）の超初歩（５）~カテゴリ変数をOne hot Encoding~

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

https://qiita.com/siruku6/items/7f3dc5a9932cd82ab860

# the previous and today's result（前回と今回の結果）

|           |やったこと                                  |score  |
|-----------|--------------------------------------------|-------|
|前回       |一部の数値特徴量を、カテゴリ特徴量へと変更  |0.14987|
|今回       |カテゴリ特徴量に対するOne hot Encodingの適用|0.14835|

# Why?

前回の記事にも記載していますが、これまでは`Label Encoding`でカテゴリ特徴量を前処理してきました。
`Label Encoding`を使うと、カテゴリデータにもかかわらず、数値の大小関係や、各数値間の間隔幅といった余分な情報がカテゴリデータに含まれてしまいます。

今回は、この余分な情報を完全にそぎ落とすために、`One hot Encoding`を使用してみることにしました。

さて、実際にやったことを見ていきます。

# 1. source cord

## 1-0. same as the previous cord

いつも通り、この「1-0.」は前回と同じソースです(^ワ^\*)
今回ここでは、

1. モジュール読み込み
1. データ読み込み
1. 数値特徴量のカテゴリ変数化
1. 特徴量の追加と削除

を行っています。
※前回と比較すると、3と4の順番が入れ替わっていますが、処理内容も前回出てきたものです。

```python
# 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. 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)

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

# 4-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. One hot Encoding

今回新しく行った部分です。
注意点としては、**学習データとテストデータの分割を行う前に、`One hot Encoding`を適用させる必要がある**ところです。

この理由は、今回使用する`pandas.get_dummies()`というメソッドにあります。
この`pandas.get_dummies()`は、これまで使用してきた`LabelEncoding`とは違い、

- 処理が`fit`と`transform`という２段階に分かれていない
- このメソッドを呼び出した時点で`One hot Encoding`が完了してしまう
- もし学習データとテストデータへとデータを分割してから`pandas.get_dummies()`を使用してしまうと、学習データとテストデータでEncodingのルール・結果が異なってしまう可能性が高い

という特徴があります。

Encodingのルールを学習データとテストデータで統一しなければ、正しく推論ができませんので、必ずデータ分割前に`pandas.get_dummies()`を適用する必要があります。
というわけでやっていきます。

5-1. 全保有データを縦方向に結合
5-2. One hot Encdoging

```python:OneHotEncoding
# 5-1. 全保有データを縦方向に結合
## 一度全データを縦方向に連結するので、
## 事前に、train(学習データ)とtest(提出用データ作成のための特徴量)を識別するための
## 'for_train'カラムを追加しておく
train['for_train'] = True
test['for_train'] = False

## データを縦方向に結合
df_all_data = pd.concat([train, test])

# 5-2. One hot Encdoging
# NOTE: 今回One hot Encodingにかける、object型の列を特定
encode_target_columns = df_all_data.columns[df_all_data.dtypes == 'object']

## One hot Encoding適用
encoded_all_data = pd.get_dummies(df_all_data, columns=encode_target_columns)
```

## 1-2. split data into training data and test data

trainとtestの分割をし、さらにtrainを学習用データと検証用データに分割します。

6-1．trainとtestの分割、そして提出用データの分割
6-2．trainの説明変数と目的変数を分割
6-3．trainを、学習データと検証用データへと分割

```python
# 6-1. split into 'train' and 'test'
encoded_train = encoded_all_data.loc[encoded_all_data['for_train'], :]
encoded_test = encoded_all_data.loc[~encoded_all_data['for_train'], :]

# 6-2. split data into explanatory variable(説明変数) and response variable(目的変数)
tmp_train_y = encoded_train['SalePrice']
encoded_train_x = encoded_train.drop(['for_train', 'SalePrice'], axis=1)
encoded_test = encoded_test.drop(['for_train', 'SalePrice'], axis=1)

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


## 1-3. same as the previous cord

ここは前回とほぼ一緒なのですが、
`LabelEncoding`を辞め、データ分割前にエンコーディングを済ませるようになったので、
データが格納されている変数名が変わっています。

7．学習model作成
8．価格予測

```python
# 7. create model（学習）
model = XGBRegressor(n_estimators=20, random_state=0)
# Old
# model.fit(x_train_label_encoded, y_train)
#
# New
model.fit(x_train, y_train)

# 8. prediction（推論）
# Old
# predict_result_for_tr_data = model.predict(x_train_label_encoded)
# predict_result = model.predict(x_test_label_encoded)
#
# New
predict_result_for_tr_data = model.predict(x_train)
predict_result = model.predict(x_test)
```

```python
# 学習データをpredictした結果
rmse_of_train = np.sqrt(mean_squared_error(predict_result_for_tr_data, y_train))
rmse_of_train

7799.889929175974 (前回: 8181.078833664238)
```

```python
# テストデータをpredictした結果
rmse_of_test = np.sqrt(mean_squared_error(predict_result, y_test))
rmse_of_test

# 相変わらずテストデータでRMSE悪化w もう気にしません
35759.51283831095 (前回: 35119.73177043754)
```

## 1-4. submission

前回は最後に、提出用データの特徴量に対しLabel Encodingしてましたが、
今回はデータ分割前にEncodingが済んでいるので、このままpredictして提出します。

※特徴量を代入した変数の名前が`encoded_test`に代わっているので注意。その他は一緒。

```python
# 9. create csv for submission
predict_submission = model.predict(encoded_test)
submission = pd.DataFrame(
    {'Id': test['Id'], 'SalePrice': predict_submission}
)
submission.to_csv('submission_new.csv', index=False)
```

この結果、スコアは`0.14987`から`0.14835`へと改善されました。
検証用データでの成績(RSME)は相変わらず悪化しているものの、予測の精度は上がっている模様。

というわけで、今回`One hot Encoding`した特徴量については、

- `LabelEncoding`よりも成績が改善
- つまり、数値としての大小関係や数値ごとの間隔幅という情報が予測に悪影響を及ぼしていた

ということがわかりました。
順位についても上位68%から上位60%まで上がってきました。


# Next step

次は、外れ値の除去か、、、、
もしくは、検証の仕方を変えてみたいですね。
テストデータでのRMSEが全くアテにならないというのは悲しい(｡ŏ﹏ŏ)


# References

- 参考文献  
[Kaggleで勝つデータ分析の技術](https://www.amazon.co.jp/dp/4297108437)


# シリーズ一覧

|No|New Trial                    |Score  |Link|Note|
|--|-----------------------------|-------|----|----|
| 1|xgboost                      |0.15663|[こちら](https://qiita.com/siruku6/items/97fdd1b12e6b18da103f)||
| 2|Label Encoding               |0.15160|[こちら](https://qiita.com/siruku6/items/48747e8c7a7f596bfa8c)||
| 3|Add and delete column        |0.15140|[こちら](https://qiita.com/siruku6/items/7b693568e6b2535e0c2a)||
| 4|Make integer into categorical|0.14987|[こちら](https://qiita.com/siruku6/items/7f3dc5a9932cd82ab860)||
| 5|One hot Encoding             |0.14835|-   |本記事|
| 6|Hyper parameters tuning      |0.13948|[こちら](https://qiita.com/siruku6/items/6dd4903f2ae68533fee6)||
| 7|Logarithimic transformation	 |0.13347||記事未作成|