データ解析を行う際のTips ・注意点

# はじめに
自身がデータ解析を行う上での注意点を書き残しておく用

- **解析を行う上でのTips**
- **解析を行う上での注意点**

＜前情報＞
今回はKaggleのコンペで使用されているデータを用いて説明します．
[Kaggle House Prices DataSet](https://www.kaggle.com/c/house-prices-advanced-regression-techniques "HousePrices Data")
[Kaggle House Prices Kernel](https://www.kaggle.com/gloomychan/house-price "HousePrices Kernel")
を用いてメモしておく.

`Memo用に残しおくため， モデル精度などは気にしていないことをご了承ください`

＜データの中身＞
特徴量は全てで81, このうちのSalePricesを目的変数( target )として解析を行います．
SalePrices以外を説明変数とします.

<更新日>
version2 : 2021.5.6


# Tips
まずデータに対してこれだけは行っておくほうがいいこと
## 前処理
まず最初にデータ(DataFrame -> df)を読み込んで `EDA (探索的データ解析)` を行います.

- .head()     : データの先頭の表示 , (5)であれば5行抽出, defalt値は5
- .info()     : データの要約 (行数、列数、各列の列名、各列に格納されるデータの型 etc....)
- .describe() : データの基礎統計量 (min, max, 25% etc.....)
- .shape[0], .shape[1] : データの行数および列数の確認
- .columns  : データのカラム名の取得
- .isnull().sum() : 各列の欠損地の個数確認
- .dtypes : 各列の型タイプを確認

```python

# import
import pandas as pd
import matplotlib.pyplot as plt    ## for drawing graph 

## load Data
df = pd.read~~~~(csv , json etc...)   

# データの先頭表示
df.head() 

# データの要約表示
df.info()

# データの次元数（何行，何列） 
print('There are {} rows and {} columns in df'.format(df.shape[0], df.shape[1]))

# データのカラム（列）取得
df.columns

# 各列データの欠損値の個数をカウント
df.isnull().sum()

# 各列データの型タイプ確認
df.dtypes
```

またデータ数が膨大な処理を行う場合，メモリ使用量を把握しておくほうが良い

```python
print(f'Training Set Memory Usage: {df.memory_usage().sum() / 1024 ** 2:.2f} MB')
```

df.columnsでカラム名を取得することができますが，　型タイプに合わせたカラム名のリストを保持しておくことで，　後々のことを考えておくと利用できる可能性がある. 下記にそのコードを記載しておく．
`includeを型タイプ（float64 etc...）`に変更することも可能．

```python
obj_columns = df.select_dtypes(include=['object']).columns
numb_columns = df.select_dtypes(include=['number']).columns
```

各型タイプごとにデータフレームを分割する場合

```python
obj_df = df[obj_columns]
numb_df = df[numb_columns]
```

## plot例

### 各列（特徴量）のヒストグラム
各列の特徴量をヒストグラム化する． しかしこれは`数値データ`のみ取り扱うことができる． `文字列データ`に対しては適用できないため， それは後々記述する．　

- .hist(bins=**, figsize=(*,*))

binsは頻度を分析する階級値の細かさの設定, figsizeは図の大きさの設定
他にも多くの引数をしてすることができるので， 下記を参照していただきたい．
[official pyplot.hist document](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.hist.html "plt hist")

```python

df.hist(bins=50, figsize=(20,15))
plt.show()
```
![スクリーンショット 2020-08-22 15.28.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482799/522dd7e6-85ba-29f3-430f-e57d44dc832b.png)

### 相関行列のヒートマップ
機械学習をする上で， 重要になってくるのが各特徴量の相関行列を求めることです． その相関行列の可視化を行うためのヒートマップで可視化する．

- .heatmap(corr, annot=bool(True or False), cmap='***')

corrは相関行列, annotはセルに値を設定, cmapは図のカラーを指定
他にも多くの引数をしてすることができるので， 下記を参照していただきたい．
[official seaborn.heatmap document](https://seaborn.pydata.org/generated/seaborn.heatmap.html "Seaborn heatmap")

```python
import seaborn as sns  ## for drawing graph 
corr = train_df.corr()
corr[np.abs(corr) < 0.1] = 0  ## corr<0.1  =>  corr=0 
sns.heatmap(corr, annot=True, cmap='YlGnBu')
plt.show()
```

![スクリーンショット 2020-08-22 15.47.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482799/3f9f7d79-68cf-5dc5-5b14-2e47d8a02d13.png)

### 特徴量の重要度の推定
どの特徴量が， targetであるSalePricesにとって重要であるかをランダムフォレスト(RandomForest)を用いて算出する．
RandomForestを行うために， まず`説明変数と目的変数`に分割する必要がある. 
**今回はtarget(SalePsices)以外の変数を全て説明変数とする．**

- .drop("***", axis=(0 or 1))  : ***には使用しない列を指定 , axis=0である場合は行, 1である場合は列
- RandomForestRegressor(n_estimators=**) : n_estimatorsは学習回数


```python

from sklearn.ensemble import RandomForestRegressor
X_train = df.drop("SalePrices", axis=1)
y_train = df[["SalePrices"]]
rf = RandomForestRegressor(n_estimators=80, max_features='auto')
rf.fit(X_train, y_train)

ranking = np.argsort(-rf.feature_importances_)  ##重要度が高い順に描画するため
sns.barplot(x=rf.feature_importances_[ranking], y=X_train.columns.values[ranking], orient='h')
plt.show()
```

![スクリーンショット 2020-08-22 16.08.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482799/60624b11-9593-68cd-7c6a-eb5802cfdf42.png)


### 各特徴量の分布（外れ値の確認用など）
回帰分析などを行う際に， 特徴量に外れ値が含まれている場合， `モデルの精度に影響しやすい`ことが言われている． そのため, `モデルの精度向上には外れ値処理が必須である`と考えられている． そのために各特徴量には, どれくらいの外れ値が含まれているかを可視化して確認することがわかりやすい．

ここでは, 特徴量の重要度上位30位までのplotを行う. 

- .iloc[:,:] : 行あるいは列を指定することで値を抽出
- sns.regplot : 2次元のデータと線形回帰モデルの結果を重ねてplot


```python

X_train = X_train.iloc[:,ranking[:30]]

fig = plt.figure(figsize=(12,7))
for i in np.arange(30):
    ax = fig.add_subplot(5,6,i+1)
    sns.regplot(x=X_train.iloc[:,i], y=y_train)

plt.tight_layout()
plt.show()
```

# 注意点
### target（目的変数）が正規分布にしたがっているかどうかの確認
今回のデータではtargetはSalePricesである． 機械学習を行うにあたって，`目的変数が正規分布に従っているかどうか`はモデルに影響するため重要とされています．そこでSalePricesの分布を見ていきます. この図は，縦軸は割合，横軸はSalePricesを示しています．


```python

sns.distplot(y_train, color="red", label="real_value")
plt.legend()
plt.show()
```
![スクリーンショット 2020-08-22 16.19.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482799/cbb3ca18-7d30-fe40-3795-902cc7d21deb.png)

上図から分布が少し左に偏っていることがわかる． 正規分布(グラフにしたときに数値の大半が中央に集中し、左右対称の釣り鐘型に「分布」するデータ)ではない.
そこで目的変数が正規分布に従うように，よく使われる**対数変換および差分変換**を下記に示します. 


```python

y_train2 = np.log(y_train)
y_train2 = y_train2.replace([np.inf, -np.inf], np.nan)
y_train2 = y_train2.fillna(0)
sns.distplot(y_train2, color="blue", label="log_value")
plt.legend()
plt.show()
```
![スクリーンショット 2020-08-22 16.19.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482799/a9771d54-fd6a-3164-d13b-64fa3e18d9c1.png)

対数変換を行うことで正規分布に近い図になっている．

```python

y_train3 = y_train.diff(periods = 1)
y_train3 = y_train3.fillna(0)
sns.distplot(y_train3, color="green", label="diff_value")
plt.legend()
plt.show()
```

![スクリーンショット 2020-08-22 16.19.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482799/010c139c-6f67-ffa4-4fc0-c05a24217ff1.png)

差分変換を行うことで正規分布と言えるような図になっている．
つまり，　モデルの精度に影響を与えにくいような目的変数は差分変換を行ったSalePricesであることが推測できる．
このように， **どのデータを取り扱う時でも，値に偏りがないかどうかを確認しておいた方が良い．**
実際に正規分布に従っている場合と従っていない場合のモデルの精度の比較を今後記事にしたいと思います. 




# まとめ
上記がデータ解析を行う上で，　まず行うことのまとめである. 
今後はデータ解析を行う上で， 先ほど述べていた文字列データの取り扱いや， 時系列データの取り扱いなどを記事にしたいと考えます.

# データ分析用記事
[モデリングのための特徴量の前処理](https://qiita.com/Shogo-dayo/items/4339532e5bc5fac3d568)