機械学習のデータ前処理備忘録

# 1.はじめに
[前回の記事](https://qiita.com/takuya_tsurumi/items/38f2858221599d5f93bd)で機械学習は「データ前処理が8割」と書きました。
今回はそのデータ前処理として私が実施していることを備忘録として書いておきます。
まだまだ至らない点等あるかと思いますので、間違いなどご指摘いただけると幸いです。



# 2.前処理の必要性

まず、全体像から把握しておくことが大切なので、
機械学習の全体像で見た前処理の立ち位置を以下図に示します。

![pasted image 0-3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329544/66436b1b-7546-bbee-5512-30f296a674ea.jpeg)
  
図.機械学習の全体像から見た前処理の立ち位置

前処理が必要な理由は以下のようなことが挙げられます。

・機械学習のモデルは文字列データではなく数値データで渡す必要があるため
・上記同様、欠損値(null)があるデータも変換しないと機械学習のモデルに渡せないため
・精度を向上させるため
・リーケージの発生を防ぐため
　　etc...


# 3.前処理の具体例

前処理は具体的に以下のようなことを実施します。
・カテゴリーデータの処理
・欠損値処理
・特徴量の変換,追加
・次元削除
　　etc...


それぞれについて詳細を書きます。
なお使用する言語は python 3 です。

## 3-1.カテゴリーデータの処理
2章で記載した通り、文字列データを数値データに変換します。
簡単な具体例を出すと、曜日データを(月:0, 火:1, 水:2, 木:3, 金:4, 土:5, 日:6) のように変換します。
この変換の際にもそのまま数値に変換するのか、One-Hot-Encordingという手法を使うのかどちらかの変換があります。
感覚的に捉えるためにそれぞれの変換を以下に示します。

元データ

|No|曜日|
|---|---|
|1|月|
|2|火|
|3|水|
|4|木|
|5|金|
|6|土|
|7|日|

①通常の数値への変換

|No|曜日|
|---|---|
|1|0|
|2|1|
|3|2|
|4|3|
|5|4|
|6|5|
|7|6|


②One-Hot-Encording

|No|曜日-(月)|曜日-(火)|曜日-(水)|曜日-(木)|曜日-(金)|曜日-(土)|曜日-(日)|
|---|---|---|---|---|---|---|---|---|---|
|1|1|0|0|0|0|0|0|
|2|0|1|0|0|0|0|0|
|3|0|0|1|0|0|0|0|
|4|0|0|0|1|0|0|0|
|5|0|0|0|0|1|0|0|
|6|0|0|0|0|0|1|0|
|7|0|0|0|0|0|0|1|


実務では、以下の記事を参考に CategoryEncorders をよく利用しています。
https://qiita.com/Hyperion13fleet/items/afa49a84bd5db65ffc31

## 3-2.欠損値処理
こちらも2章で記載した通り、欠損値(null)があるデータを変換、もしくは削除します。
欠損値処理を実施する際に注意しなければならないことがあります。

それは、「null に意味のあるデータかどうか」という点です。
nullに意味がなければ変換または削除はしやすいですが、
null自体に意味を持たせてしまっているパターンがあります。

そのため欠損値の処理は安易に実施するのではなく、
どんな処理が必要なのか確認しつつ、実施しましょう。

こちらは pandas の drop, dropna, fillna 関数を使用すると簡単に実装することが可能です。

例として、初心者の入門問題としてよく使用される、
[kaggleのタイタニック号の生存予測データ](https://www.kaggle.com/c/titanic)で考えてみます。

タイタニック号データの欠損値の数は、以下のようになっています。

~~~
PassengerId      0
Survived         0
Pclass           0
Name             0
Sex              0
Age            177
SibSp            0
Parch            0
Ticket           0
Fare             0
Cabin          687
Embarked         2
~~~

AgeとCabinとEmbarkedに欠損値が含まれています。

dropna関数を使用すると一括削除が可能です。
drop関数を使用すると指定した行・列の削除が可能です。
fillna関数を使用すると穴埋めすることが可能です。

実際に欠損値データを削除した場合、欠損値の一覧は
以下のように"Age","Cabin","Embarked"が削除されます。

~~~
PassengerId      0
Survived         0
Pclass           0
Name             0
Sex              0
SibSp            0
Parch            0
Ticket           0
Fare             0
~~~

欠損値に何かしらの値を代入した場合は、"Age","Cabin","Embarked"の欠損値が0に

~~~
PassengerId      0
Survived         0
Pclass           0
Name             0
Sex              0
Age             0
SibSp            0
Parch            0
Ticket           0
Fare             0
Cabin           0
Embarked         0
~~~

以下公式ドキュメントを貼っておきます。ご参考まで。

drop関数
https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.drop.html

dropna関数
https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.dropna.html

fillna関数
https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.fillna.html

## 3-3.特徴量の変換,追加
こちらは、精度の向上とリーケージの発生を防ぐために実施します。
実施することは以下のようなことです。
・連続値を離散値へ変換する
・ある説明変数を使用して、別の説明変数を作成する

こちらもタイタニック号のデータで考えます。
タイタニック号でよく使われている特徴量変換は、何名で一緒に乗船しているかというデータを
複数人で船に乗っているなら1、1人で船に乗っている場合0に変換します。

## 3-4.次元削除
こちらも、モデルの精度向上のために実施します。
また、モデルの計算量が莫大である際にも計算量を減らすために実施します。

3-2で記載したdrop関数を使って次元を減らしていきます。
また、重要な特徴量をRandomForestを使って見分けることもできます。

# 4,まとめ
前処理の実施内容を書きました。
内容としてはそこまで多くもないですが、実際はどの特徴量が重要で、
どの特徴が必要で、どの特徴量が必要でないか、試行錯誤を繰り返していくため、
とても時間がかかります。

また、業界のドメイン知識も大変重要になりますので、
業界知識を持った方との打ち合わせは重要です。