【1日1写経】Predict employee attrition【Daily_Coding_001】

## 初めに
- 本記事は、python・機械学習等々を独学している小生の備忘録的な記事になります。
- 「自身が気になったコードを写経しながら勉強していく」という、きわめてシンプルなものになります。
- 建設的なコメントを頂けますと幸いです（気に入ったら`LGTM` & `ストック`してください）。

## お題:IBM HR Analytics Employee Attrition & Performance
- 今回のお題は、**IBM HR Analytics Employee Attrition & Performance**です。kaggleに記載のあった説明によると、**「従業員の退職理由」**を探る問題のようです。
- 今回、以下のyoutubeの動画を見つつ写経していきました。

Link：[Predict Employee Attrition Using Machine Learning & Python](https://www.youtube.com/watch?v=CpCs--DpJh0&t=144s)

データはkaggleから取ってきました。

Link:[IBM HR Analytics Employee Attrition & Performance](https://www.kaggle.com/pavansubhasht/ibm-hr-analytics-attrition-dataset) 

分析はyoutubeの動画にある通り、Google Colaboratryを使用しました（便利な時代になったものです）。

### Step 1：データ読み込み～内容確認
それではやっていきたいと思います。

#### 1.1: ライブラリのインポート

```python
# ライブラリの読み込み
import numpy as np
import pandas as pd
import seaborn as sns
```
基本となるライブラリを読み込みます。必要なライブラリはこの後も適宜追加していく感じになります。

次にデータの読み込みですが、kaggleのサイトからダウンロードしたcsvファイルをgoogle colabで読み込みます。


#### 1.2: Google Colabへのファイルのアップロード

```python
# データのアップロード
from google.colab import files
uploaded = files.upload()
```

これをすることによって、ローカルに保存してあるファイルをgoogle colab上に取り込むことができます。
普段はGoogle Driveにファイルのアップロードしてから、Google Driveを連携させて読み込んでいたので、こっちのほうが楽でいいですね。

#### 1.3: pandasでの読み込み
アップロードしたデータを読み込んでいきます。

```python
# データの読み込み
df = pd.read_csv('WA_Fn-UseC_-HR-Employee-Attrition.csv')

# データの確認
df.head(7)
```

おなじみのコードですね。次からはデータの中身を確認していきます。

#### データの中身の確認
以下のコードは（実際には）それぞれ別々に実行していますが、ここでは纏めて書いておきます。

```python
# データフレームの行・列の数確認
df.shape

# それぞれの列の中身のデータ型を確認
df.dtypes

# 欠損値の確認
df.isna().sum()
df.isnull().values.any()

#基礎統計量の確認
df.describe()

# 退職者と在籍者数の数の確認（被説明変数の数の把握）
df['Attrition'].values_counts() #図1

# 退職者と在籍者数の可視化
sns.countplot(df['Attrition'])

# 年齢別の退職者数と在籍者数の可視化
import matplotlib.pyplot as plt
plt.subplots(figsize=(12,4))
sns.countplot(x='Age', hue='Attrition', data=df, palette='colorblind') #図2

```
【図1】
![Attrition1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395696/ad44baa0-0896-5902-3d6f-055b029b95e2.png)

【図2】
![Attrotion2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395696/ea54396a-2172-a204-8fc2-c9775e6f492f.png)


ここまではいつもやるようなデータの確認です。まずしっかりデータの中身を確認することはやはり必要だと思います。

#### 1.4: object型のユニーク値の確認

次に、先ほど確認したデータ型のうちobject型の列のユニーク値を確認します。

```python
for column in df.columns:
  if df[column].dtype == object:
    print(str(column) + ':' + str(df[column].unique()))
    print(df[column].value_counts())
    print('___________________________________________')
```

1. 1行目：forループでそれぞれの列を繰り返しとってくる
2. 2行目：取ってきた列がobject型か判定
3. 3行目：カラム名 + そのカラムのユニーク値を出力
4. 4行目：各ユニーク値の個数を出力

#### 1.5: 不要行の削除
_`.drop()`_で予測するのに意味をなさない列を削除します。

```python
df = df.drop('Over18', axis=1)
df = df.drop('EmployeeNumber', axis=1)
df = df.drop('StandardHours', axis=1)
df = df.drop('EmployeeCount', axis=1)
```
これは説明不要ですね。退職する理由にならないものをdfの中から外します。

#### 1.6: 列間の相関の確認
これもお馴染みの処理だと思います。各列間の相関（_`correlation`_）を確認し、_`heatmap`_可視化します。

```python
df.corr()

plt.figure(figsize=(14, 14))
sns.heatmap(df.corr(), annot=True, fmt='.0%')
```

今回、heatmapを作成する際に指定しているのは以下の2つです。

| Item | Description |
|:-:|---|
| annot | True に設定すると、セルに値を出力します。 |
| fmt | annot=True に設定した場合、またはデータセットを指定した場合の出力フォーマットを文字列で指定 |

参考：[Seaborn でヒートマップを作成する](https://pythondatascience.plavox.info/seaborn/heatmap)

#### 1.7: sklearnでカテゴリカル(non numerical)データにラベリング
```python
from sklearn.preprocessing import LabelEncoder

for column in df.columns:
    if df[columen].dtype == np.number:
        continue
    df[column] = LabelEncoder().fit_transform(df[column])
```
ここでは、sklearnのLabelEncoderを使ってobject型だったデータに数値データに置き換える（「分類器にかける前に文字データを離散値(0, 1, ・・・)に変換」）。

置き換えが終わったら、dfの列の順番を入れ替え分析しやすい形にします。

```python
# Ageを新しい列に複製
df['Age_Years'] = df['Age']

# Age列を落とす
df = df.drop('Age', axis=1)
```

### Step2: sklearnで分析
ここからが本番ですね（前処理が重要なのは言うまでもないですが）。

```python
# dfをデータを説明変数と被説明変数に分割
X = df.iloc[:, 1:df.shape[1]].values
Y = df.iloc[:, 0].values

# 訓練データ、教師データのテストデータサイズ（25%）で分割
from sklearn.model_selection import train_test_split
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.25, ramdom_state = 0)

# ランダムフォレストによる分類
from sklearn.ensemble import RandomForestClassifier
forest = RandomForestClassifier(n_estimators = 10, criterion = 'entropy', random_state = 0)
forest.fit(X_train, Y_train)
```
上から見ていきましょう。

- _`iloc[]`_を使って説明変数と被説明変数を分離します。
- sklearnの_`train_test_split`_を使って訓練データとテストデータの分割。
train_test_splitの引数は以下の通りです。

| Item | Description |
|---|---|
| arrays | 分割対象の同じ長さを持った複数のリスト、Numpy の array, matrix, Pandasのデータフレームを指定。 |
| test_size | 小数もしくは整数を指定。小数で指定した場合、テストデータの割合を 0.0 〜 1.0 の間で指定します。整数を指定した場合は、テストデータに必ず含めるレコード件数を整数で指定します。指定しなかった場合や None を設定した場合は、train_size のサイズを補うように設定します。train_size を設定していない場合、デフォルト値として 0.25 を用います。 |
| train_size | 小数もしくは整数を指定。小数で指定した場合、トレーニングデータの割合を 0.0 〜 1.0 の間で指定します。整数を指定した場合は、トレーニングデータに必ず含めるレコード件数を整数で指定します。指定しなかった場合や None を設定した場合は、データセット全体から test_size を引いた分のサイズとします。 |
| random_state | 乱数生成のシードとなる整数または、RandomState インスタンスを設定します。指定しなかった場合は、Numpy のnp.random を用いて乱数をセットします。 |
(参照: [scikit-learn でトレーニングデータとテストデータを作成する](https://pythondatascience.plavox.info/scikit-learn/%e3%83%88%e3%83%ac%e3%83%bc%e3%83%8b%e3%83%b3%e3%82%b0%e3%83%87%e3%83%bc%e3%82%bf%e3%81%a8%e3%83%86%e3%82%b9%e3%83%88%e3%83%87%e3%83%bc%e3%82%bf))

- ランファムフォレストを使って分類をします。ここでの引数は、

n_estimators: 木の数の指定(デフォルトは100)
criterion: `gini` or `entropy`を指定（デフォルトは`gini`）

この後、`forest.fit(...)`でモデルを学習させます。

それでは精度を見ていきましょう。

```python
forest.score(X_train, Y_train)
```

この後、_`confusion_matrix`_(混合行列)を使って`Accuracy`を計算してます。

```python
from sklearn.metrics import confusion_matrix

cm = confusion_matrix(Y_test, forest.predict(X_test)) #cm: confusion_matrix

TN = cm[0][0]
TP = cm[1][1]
FN = cm[1][0]
FP = cm[0][1]

print(cm)
print('Model Testing Accuracy = {}'.format( (TP + TN) / (TP + TN + FN + FP)))
```

以上で、簡単ではありますがskleanを使って二値分類の写経になります。

##　最後に
内容的にはそこまで難しいものではないにせよ、まだまだ理解していない箇所があることが認識できましたので、引き続き勉強していきたいと思います。


以上。