[kaggle / python] 回帰問題（house prices）の超初歩（３）~特徴量の追加と削除~

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

https://qiita.com/siruku6/items/48747e8c7a7f596bfa8c

# Summary of the previous article（前回のあらまし）

前回は、kaggle の House Prices の課題に対して、
「`Label Encoding`を使用して、初期状態で存在する特徴量全てを`xgboost`の学習データとして活用する」
というところまでやりました。

Label Encodingを使用した結果、順位が上位68% -> 64%へと`4%上昇`しました。
Scoreでいうと、`0.15663`->`0.15160`という変化がありました。


# Today's result

今回は、特に難しいことはしないのですが、特徴量を新しく作成して追加したり、不要と考えられる特徴量を削除してみます。


|           |やったこと  |score  |
|-----------|------------|-------|
|前回       |-           |0.15160|
|今回 Part.1|特徴量の追加|0.15058|
|今回 Part.2|特徴量の削除|0.15140|

今回は2つ作業をしたので、二つスコアが出てます。

二つ目の結果は、scoreが下がっているのですが....
この操作は通常行うべきものと考えられるため、scoreが下がったとしてもこれは実施することにしました。
理由は後半で書こうと思います。


さて、具体的に何をしたのか見ていきましょう～


# Part.1
## 1. source cord

### 1-0. same as the previous cord

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

1. モジュール読み込み
1. データ読み込み

です。

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

# 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')
```

### 1-1. add new column

今回はここで、新しいカラム（特徴量）を追加します。
物件の1階の広さ、2階の広さ、地下の広さを足し合わせた特徴量として`TotalBsmtSF`というデータを生成します。

```python
# add new column
train['totalRoomsSF'] = train['1stFlrSF'] + train['2ndFlrSF'] + train['TotalBsmtSF']
test['totalRoomsSF'] = test['1stFlrSF'] + test['2ndFlrSF'] + test['TotalBsmtSF']
```

### 1-2. same as the previous cord

そして実は、以降は全く同じソースとなりますｗ
つまり、先ほどの`TotalBsmtSF`という特徴量を作成することによる効果を今回確認する形になります。

以降は前回と同じコードですが、何はともあれ、一応同じコードも書いておきます。


1. 説明変数と目的変数の分割
1. 学習データとテストデータの分割
1. LabelEncoding
1. 学習model作成
1. 価格予測
1. kaggleに提出(submission)するデータの生成

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

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

```python
# 3. LabelEncoding
from sklearn.preprocessing import LabelEncoder

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

# ※注意
df_all_data = pd.concat([x_train, x_test, test])

# これらのstr型のcolumnはそのままだとはXGBoostに投入できない
not_expected_type_column_names = train.columns[train.dtypes == 'object']

for col_name in not_expected_type_column_names:
    # nanを'NaN'という文字列に置換してます
    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)
```

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

# 5. prediction（推論）
predict_result_for_tr_data = model.predict(x_train_label_encoded)
predict_result = model.predict(x_test_label_encoded)
```


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

7681.997640750206 (前回: 8146.100714171564)
```

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

30411.796986629754 (前回: 33584.266159693645)
```

```python
# 6. create csv for submission
test_encoded = test.copy()

for col_name in not_expected_type_column_names:
    # ここでも、全データをlabel encoderのfitに食べさせるのを忘れずに！
    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.15160`から`0.15058`へと改善されました！
順位は記録取ってなかったのですが、大して上がりませんでした(๑>؂<๑)

# Part.2

Part.2は、変更した箇所だけを書きます！

## 1. source cord
### 1-1. drop columns

今回ここで、`Id`列を特徴量から削除します。
Part.1の`# add new column`のところで、`totalRoomsSF`という特徴量を作成しましたが、この直後で`Id`列をdropします。


```python
# add new column
train['totalRoomsSF'] = train['1stFlrSF'] + train['2ndFlrSF'] + train['TotalBsmtSF']
test['totalRoomsSF'] = test['1stFlrSF'] + test['2ndFlrSF'] + test['TotalBsmtSF']

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

注意点としては、この処理のために変数`test`から`Id`列が無くなってしまいますので、kaggleへのsubmit用csvを作成する際の一列目には、`test_ID`を指定しましょう。
以下のような感じです。

```python
submission = pd.DataFrame(
    # {'Id': test['Id'], 'SalePrice': predict_submission} <- コレでは動かない
    # test['Id']は削除してしまったので、test_IDを使う
    {'Id': test_ID, 'SalePrice': predict_proto2}
)
submission.to_csv('submission_new.csv', index=False)
```

この結果、スコアは`0.15058`から`0.15140`へと悪化してしまいます...

## 2. Why drop Id?

データ分析の経験が少ない私ですが、Id列を削除した理由を書いてみます。

Id列は、通し番号のようなものです。
データ分析一般に視野を広げて考えると、基本的にはId自体に意味はないはずです。

ただ、今回の House Price においては、価格を決める重要な特徴量として作用していたようです。
XGBoostのモジュールで、特徴量ごとの重要度を表示できるのですが、以下のとおりです。

```python
from xgboost import plot_importance
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline

ax = plot_importance(model, max_num_features = 100)
fig = ax.figure
fig.set_size_inches(10, 30)
```


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327407/74d7426e-04c2-2230-ab95-acae58fd0503.png)

「ポイントが高い方ければ高いほど、価格の推論にとって重要だった」ということを示しているので、これだけを見ると、Idは価格を決める上でかなり重要な値として作用していたことがわかります。。。
実際、データベースに登録された順番が意味を持つような大規模データなどもなくはないと思います。
そういう場合は、Id列を残しておくことで良い結果をもたらすこともあるでしょう。

しかし、データというものを一般的に考えれば、多くの場合はIdが意味を持つことはなく、推論の邪魔になると考えられるためにId列は削除されることが多いはずです。

このkaggleの学習では、点数を高めるためになんでもする、ということは目指しません。
あくまでもデータ分析一般に活用可能な方法を、一つ一つ学んでいくことを目指しています。
そんな経緯もあり、点数が下がったとしてもId列を削除して分析を進めることにしました。


# Next step

次は何をしようか...
いくつか試してみて、うまくいった方法で記事を書いてみようと思う

# 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|-   |本記事|
| 4|Make integer into categorical|0.14987|[こちら](https://qiita.com/siruku6/items/7f3dc5a9932cd82ab860)||
| 5|One hot Encoding             |0.14835|[こちら](https://qiita.com/siruku6/items/48e70607dc16746ec280)||
| 6|Hyper parameters tuning      |0.13948|[こちら](https://qiita.com/siruku6/items/6dd4903f2ae68533fee6)||
| 7|Logarithimic transformation	 |0.13347||記事未作成|