[kaggle / python] 回帰問題（house prices）の超初歩（１）

# What is this? (これは何？)

kaggle の [House Price](https://www.kaggle.com/c/house-prices-advanced-regression-techniques)に取り組みます。

- 狙い  
kaggleの初心者向け記事はたくさんありますが、あんまり初心者向けじゃない気がしています。（私のレベルが低すぎて...）  
なので、より低レベルな内容に絞った記事（メモ）を作ろうとしました。  


# What is the skill of writer?（書いた人の技術力はどんなもの？）

記事を書いた人の能力は以下の通り（2021年05月16日時点）
状況が近い人は、何か参考になるものがあるかもしれません

- **学校**：文系大卒
- **お仕事**：WEBアプリケーションエンジニア歴満4年  
その他のエンジニア経歴はなし  
- **言語**：Python：基本的な文法は把握済み／仕事でも利用  
- **数学**：Ⅲ/Ｃを習ってない  
年代が違う人のために説明すると、微分方程式や行列は習っていない、ということです（年齢がばれる...）

理系の大学を出た人には何の参考にもならないのではないかと思っています...


# First step! (はじめの一歩)

## 0. What do you do?（何する？）

はじめの一歩は、ほぼ全く前処理せずに以下の分析フローを一通り流してみました。

- 1-1. データ取得（読み込み）
- 1-2. 学習データと検証用データの分割
- 1-3. データ整形  
分析可能にするための最低限のもののみ
- 1-4. モデルを学習させる  
手を抜くためにXGBoostを使う
- 1-5. 予測してみる  
学習させたXGBoostモデルで検証用データに対する予測を行い、成績を確認
- 1-6. submit（提出）

この記事では、とりあえずここまでやりました。
前処理は、本当に何もしてないです。それでも、ものすごいつまづきました。
それでも諦めない(｀･ω･´)根性

## 1. Source cord

### 1-0. import modules

今回使ったモジュールです

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

### 1-1. get data （データ取得（読み込み））

```python
import os
for dirname, _, filenames in os.walk('/kaggle/input'):
    for filename in filenames:
        print(os.path.join(dirname, filename))

# kaggleなので、csvファイルがこのpathになってます
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')
train.head()

# 処理結果
/kaggle/input/house-prices-advanced-regression-techniques/sample_submission.csv
/kaggle/input/house-prices-advanced-regression-techniques/data_description.txt
/kaggle/input/house-prices-advanced-regression-techniques/train.csv
/kaggle/input/house-prices-advanced-regression-techniques/test.csv
Id	MSSubClass	MSZoning	LotFrontage	LotArea	Street	Alley	LotShape	LandContour	Utilities	...	PoolArea	PoolQC	Fence	MiscFeature	MiscVal	MoSold	YrSold	SaleType	SaleCondition	SalePrice
0	1	60	RL	65.0	8450	Pave	NaN	Reg	Lvl	AllPub	...	0	NaN	NaN	NaN	0	2	2008	WD	Normal	208500
1	2	20	RL	80.0	9600	Pave	NaN	Reg	Lvl	AllPub	...	0	NaN	NaN	NaN	0	5	2007	WD	Normal	181500
2	3	60	RL	68.0	11250	Pave	NaN	IR1	Lvl	AllPub	...	0	NaN	NaN	NaN	0	9	2008	WD	Normal	223500
3	4	70	RL	60.0	9550	Pave	NaN	IR1	Lvl	AllPub	...	0	NaN	NaN	NaN	0	2	2006	WD	Abnorml	140000
4	5	60	RL	84.0	14260	Pave	NaN	IR1	Lvl	AllPub	...	0	NaN	NaN	NaN	0	12	2008	WD	Normal	250000
5 rows × 81 columns
```

カラムはどうなってるのかな

```python
train.columns

# 結果：こんなにたくさんカラムが...
Index(['Id', 'MSSubClass', 'MSZoning', 'LotFrontage', 'LotArea', 'Street',
       .... (略) ....
       'SaleCondition', 'SalePrice'],
      dtype='object')
```

欠損値も見ておこう

```python
train.isnull().sum()[train.isnull().sum() > 0].sort_values(ascending=False)

PoolQC          1453
MiscFeature     1406
Alley           1369
Fence           1179
.... (略)
```

欠損値の確認は、この記事を参考にしました

https://yolo-kiyoshi.com/2018/12/17/post-1003/

### 1-2. 学習データと検証用データの分割

まず、予測したい値（いわゆる目的変数）を分離しておきます。
House Priceの場合は、`SalePrice`が予測したい値みたいですね。

```python
proto_x = train.drop('SalePrice', axis=1)
proto_y = train['SalePrice']
```

`proto`というprefixをつけたのは、お試しだからです。
深い意味はないです。

次に、学習に使うデータと、検証(test)に使うデータを分割します

```python
x_train, x_test, y_train, y_test = \
    train_test_split(proto_x, proto_y, test_size=0.20, random_state=0)
```

### 1-3. データ整形  

さーて、試しにこれで学習してみようかなぁああ！！(^ワ^\*)

```python
model = XGBRegressor(n_estimators=20, random_state=0)
model.fit(x_train, y_train)
```

結果...さすがに気が早かったか....

```python
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-36-1c4e8355ea5e> in <module>
      1 model = XGBRegressor(n_estimators=20, random_state=0)
----> 2 model.fit(x_train, y_train)

.... (略) ....

ValueError: DataFrame.dtypes for data must be int, float or bool.
                Did not expect the data types in fields MSZoning, Street, Alley, 
.... (略) ....
PoolQC, Fence, MiscFeature, SaleType, SaleCondition

```

`model.fit()`に渡すDataFrame内の値は、`int, float, bool` のうちどれかでなければいけないらしい。
この条件に反するfield名は、 `MSZoning, Street, ...., SaleCondition`ということのようだ。

全く前処理しない場合の成績を見てみたかったが、これらのフィールドについては、消すか変換してあげないといけない。

「前処理なし」により近いのは「消す」方かな！(^ワ^\*)keshichae!!

ちなみに、これらはどれもobject型なので、object型のカラム名を特定した上で消そう(\*^^\*)


```python
# NOTE: object型の列を特定
not_expected_type_column_names = x_train.columns[x_train.dtypes == 'object']

# 消えてしまえ！
# _0 というpostfixは、これからたくさんチャレンジするつもりなのでつけてみた
x_train_0 = x_train.drop(not_expected_type_column_names, axis=1)
x_test_0 = x_test.drop(not_expected_type_column_names, axis=1)
x_train_0.head()

# 結果: 81列あったデータが37列まで減った。スッキリ！（）
Id	MSSubClass	LotFrontage	LotArea	OverallQual	OverallCond	YearBuilt	YearRemodAdd	MasVnrArea	BsmtFinSF1	...	GarageArea	WoodDeckSF	OpenPorchSF	EnclosedPorch	3SsnPorch	ScreenPorch	PoolArea	MiscVal	MoSold	YrSold
618	619	20	90.0	11694	9	5	2007	2007	452.0	48	...	774	0	108	0	0	260	0	0	7	2007
870	871	20	60.0	6600	5	5	1962	1962	0.0	0	...	308	0	0	0	0	0	0	0	8	2009
92	93	30	80.0	13360	5	7	1921	2006	0.0	713	...	432	0	0	44	0	0	0	0	8	2009
817	818	20	NaN	13265	8	5	2002	2002	148.0	1218	...	857	150	59	0	0	0	0	0	7	2008
302	303	20	118.0	13704	7	5	2001	2002	150.0	0	...	843	468	81	0	0	0	0	0	1	2006
5 rows × 37 columns
```

### 1-4. モデルを学習させる

```python
model = XGBRegressor(n_estimators=20, random_state=0)
model.fit(x_train_0, y_train)

# 結果: なんか出たぽよ～
XGBRegressor(base_score=0.5, booster=None, colsample_bylevel=1,
             colsample_bynode=1, colsample_bytree=1, gamma=0, gpu_id=-1,
             importance_type='gain', interaction_constraints=None,
             learning_rate=0.300000012, max_delta_step=0, max_depth=6,
             min_child_weight=1, missing=nan, monotone_constraints=None,
             n_estimators=20, n_jobs=0, num_parallel_tree=1,
             objective='reg:squarederror', random_state=0, reg_alpha=0,
             reg_lambda=1, scale_pos_weight=1, subsample=1, tree_method=None,
             validate_parameters=False, verbosity=None)
```

### 1-5. 予測してみる

```python
# 予測結果の出力

# 学習に使ったデータに対しての予測
predict_result_for_tr_data = model.predict(x_train_0)

# 検証用データに対しての予測
predict_result = model.predict(x_test_0)
```

結果は以下の通り。
[House Priceという課題では、RMSE(Root Mean Squared Error)という指標で成績を評価する](https://www.kaggle.com/c/house-prices-advanced-regression-techniques/overview/evaluation)らしいので、`RMSE`を計算する。

```python
# 学習に使ったデータに対してモデルを適用した場合の誤差
rmse_of_train = np.sqrt(mean_squared_error(predict_result_for_tr_data, y_train))
rmse_of_train

# 結果
>> 9283.227477186625
```

```python
# 検証用データに対してモデルを適用した場合の誤差
rmse_of_test = np.sqrt(mean_squared_error(predict_result, y_test))
rmse_of_test

# 結果
>> 37010.13476747102
```

test も train もそうですが、`RMSE`値が小さい値になるほど、より正解に近い予測ができているといえるようです。
なので、上記「9283.22..., 37010.13...」よりも小さい値を目指す感じですね(｀･ω･´)


### 1-6 submit

一応提出してみます。
kaggleの点数を上げたいわけではないのですが、どのくらい実力が上がっているのか、後で参考になると思うので、最低ラインの場合の実力を確認。

ここは特に解説しませんが、コードは載せておきます。

```python
# trainデータに対して実施したのと同じ手順で、1-3 ~ 1-4を実施します
# 1-3. データ整形
# NOTE: x_trainでobject型だった列を特定して削除
not_expected_type_column_names = x_train.columns[x_train.dtypes == 'object']
test_proto = test.drop(not_expected_type_column_names, axis=1)

# 1-4. モデル学習
#   モデルはさっき学習させたもの(model)を使います！

# 1-5. 予測
predict_proto = model.predict(test_proto)
submission = pd.DataFrame({'Id': test['Id'], 'SalePrice': predict_proto})
submission.to_csv('submission_new.csv', index=False)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327407/2d584816-ebb3-ea2c-04cf-8e52a5e4c4ae.png)

なんか、何もしてないのに上位68%に入った...`XGBoost`しゅごい...(\*´﹃｀\*)

## 2. Result （結果と所感）

これが正真正銘の最低ライン。完全に手抜きした（完全に何もしなかった）場合の結果。
ここからどこまで改善できるか。

個人的には、今回試した6ステップを、何も見ずにそらでできるようになるのが、まず最初のステップと捉えています。

結果を出すためには細かい手順も必要なのですが、それらは結局課題によって異なるので、まずはなるべく幅広い課題に使えるものから試しつつ覚えていきたいと思います。


# Next step

次回以降は、

1. カテゴリ変数を数値化  
カテゴリ変数をXGBoostで使えるようにする  
`pd.get_dummies()`で簡単にできるみたいです
1. 欠損値の穴埋め

あたりをやってみて、成績が多少変わるかどうか試してみようと思います。

ハイパーパラメータの探索や、他のモデルのお試しなどはもっと先になると思いますが、やってみたいと思います。

ド素人なので、ひとまずはこの課題で上位50パーセント以内に入ることを目指します ( \*˙︶˙\*)وｸﾞｯ!


# Reference（参考にしたもの）

https://www.amazon.co.jp/dp/4297108437

この本は、様々な手法を広く浅く辞書的に掲載した本になっている気がします。

コードを書きながら1歩1歩学んでいく系の本ではないです。
なので、前から順に読んでいっても、全然書けるようにならないです(´；ω；｀)ﾌﾞﾜｯ

ただ、初心者向けなのか、私レベルの人でもとても理解しやすかったです！


# さいごに....

もうちょっと、もうちょっと情報量の少ない記事にしたい...
できれば3~5スクロールで最後まで見える記事がいい....
あ、喋るの好きだからムリかな....(\*´﹃｀\*)

# シリーズ一覧

|No|New Trial                    |Score  |Link|Note|
|--|-----------------------------|-------|----|----|
| 1|xgboost                      |0.15663|-   |本記事|
| 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|[こちら](https://qiita.com/siruku6/items/48e70607dc16746ec280)||
| 6|Hyper parameters tuning      |0.13948|[こちら](https://qiita.com/siruku6/items/6dd4903f2ae68533fee6)||
| 7|Logarithimic transformation  |0.13347||記事未作成|