「”すぐ” このデータまとめて欲しい」に ”すぐ”に超簡単に応えられる Python の・・・（DataPrep+Boruta-SHAP）

**～機械学習で 特徴量を絞って決定木を描くまでを自動化 してみる（問題解決に機械学習を活用：AutoEDA、Boruta-SHAP）〜**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/3013315b-bf45-8c9f-3390-65459a18dea9.png)

2022/05/23
- detaprep（AutoEDA）データレポートのセル内展開 → html出力に（見やすさ、操作しやすさはこちらの方がいい）
- GoogleColabのフォーム入力値を引数とした前処理 → 引数をpd.Series化
- 実データで適用したら。。。現実データはあまりにゴミが多く、この程度の前処理ではダメだなと痛感。前処理は深い。

## はじめに
Pythonには、数多くのAutoEDAツールがあり、これらはとても便利です。
これらは、データの基本統計量や関係性を ”すぐに、超簡単に” 確認することができますので問題解決などで大きな助けになります。

ただ、これだけで問題解決が図れるか？というと、現実はそうあまくありませんので、今回はもう少し踏み込んだ **AutoEDA＋アルファの活用を考えてみたい**と思います。

データの基本統計量や関係性をみたり、示したりするだけではなく、もう少し踏み込んた状況を ”すぐに、超簡単に” 把握できないか？ということです。
具体的には「機械学習を利用し、原因の糸口につながるデータ傾向を手間をかけずにシンプルに把握できないか？」ということです。




## 問題解決の現状

例えば、
ある製造ロットで不良品が数多く発生した場合は、以下のようなことを確認します。

>- 4Mに変化はなかったか？
>- 不良現品に何らかの異常は認められないか？
>- 検査データの傾向に差異や異常はないか？

よく「変化点」といわれますが、①時系列変化に異常はないか、②特定ロットのデータは異常にばらついていないかを確認して、差異（変化）が発見できると、原因特定に一歩近づくことができます。
※ 管理図でいえば、「群間変動」「群内変動」ですね。
　
　
①時系列変化に異常はないか という面では、「時系列データ分析」「異常検知」などで機械学習が活用されており、以前に記事でも取り上げました。

https://qiita.com/hima2b4/items/6c3e57fa078d6db5dc4f

このような時系列分析が原因特定に結びつく場合はよいですが、データの関係性を分析しないとよくわからないこともあります。

機械学習は、原因を教えてくれるというものではありませんが、糸口につなげる活用はできるはずです。

手間がかかることはできるだけ機械にまかせ、効率的に進めたいと思いますが、データを扱う前に処理しないといけない課題が数多くあります。例えば以下のような内容です。

>- HEXデータがある。（16進数）
>- 欠損データがある。（入力忘れ、ミスといった単純なものだけではなく、検査でNGとなると、その時点で検査を停止するので以降は欠損値となるという場合もあります。）

また、データカラム（特徴量）が多い場合、統計量や分布、相関等をまとめること等、概要を把握するだけでも大変ということもあります。


## 問題解決の課題

これらの現状を受け、
先の通り「機械学習を利用し、原因の糸口につながるデータ傾向を手間をかけずにシンプルに把握する」ことを目的としたものを検討してみることにしました。

できれば、
>- データ概観・可視化は機械まかせとしたい。
>- 前処理も機械まかせで自動化したい。
>- データカラム（特徴量）を精度は落とさぬ範囲で、できるだけダイエットしたい。
>- 目的変数と説明変数の関係性をわかりやすく表現したい。

これらができれば、原因特定プロセスの生産性向上が図れるのではないかと思います。


## 実行条件など

>- **Google colab**で実行
>- 実行コードは [**ココ**：GitHub](https://colab.research.google.com/github/hima2b4/Auto_Profiling/blob/master/EDA%E2%87%92%E7%89%B9%E5%BE%B4%E9%87%8F%E9%81%B8%E6%8A%9E%E2%87%92%E6%B1%BA%E5%AE%9A%E6%9C%A8.ipynb) にあります。（これはDashの除いたVer.です。）


## データ概観

まず、データ概観です。

Pandasで処理を行う場合は、以下が定番かと思います。

|  概観 |  概観内容  | 備考 |
| ---- | ---- | ---- |
|  df.shape  | データフレームの行列数  |  (行,列) |
|  df.head()  | データフレーム先頭5行データ | 表形式表示 |
|  data.info()  | カラム名、データ数、欠損値、データ型 |  non-nullは欠損していない数 |
|  df.describe(include='all')  | データ数、平均値、標準偏差、最小値、第一四分位数、中央値、第三四分位数、最大値などの基本統計量 | カテゴリーデータは、unique数、最も多いデータ:top とそのデータの個数:freq |

Pythonには便利なEDAライブラリが数多くありますので、今回はEDAライブラリを活用したいと思います。
私は、シンプルでインタラクティブなAutoViz with bokeh、DataPrepをよく利用しています。
今回は動きが軽く、データ概観・可視化を一発で実行してくれる DataPrep を使うことにしました。

https://github.com/sfu-db/dataprep

DataPrepは、様々な切り口でデータを確認することができますが、今回はデータ概要をひとまとめに出力してくれる [**create_report**](https://docs.dataprep.ai/_downloads/1a61c6aebb3ecbe9dc9742bd6ca78ddb/titanic_dp.html) のみを実行させています。




　
## 前処理

「原因の糸口につながるデータ傾向を手間をかけずにシンプルに把握すること」を目的としていますので、このような場合の前処理には適度なバランスがあるように思います。　
　

今回は、決定木のモデルで処理することを前提に、以下の方針で進めることにしました。

>- IDカラム等、分析に不要なデータ列を削除できるようにする。
>- 欠損値 比率が高いデータ列があれば列を削除する。
>- 欠損値がある行を削除する。（補完処理はせず、欠損値を含む行は消去する。)
>- 16進数データがあれば10進数に自動変換する。（以下参考記事）
>- カテゴリーデータをエンコーディングする。（ラベルエンコーディングに絞る。スケーリングも行わない。）
>- 文字列データは、ラベルエンコーディングする場合を除いて消去する。
>- 日本語カラムは英語翻訳できるようにする。
>- それぞれの前処理は、実行するかしないかを選択できるようにする。

https://qiita.com/hima2b4/items/4b023797fcf245cbb587

以下は、前処理を実行するセルのキャプチャの一部です。
Google colabのフォームを利用しました。
![前処理の選択.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/3e260bf4-4b6f-0a87-40a7-ec5c8d18ca5f.jpeg)


## 特徴量選定

次に特徴量の選定です。
これもさまざまな方法が提案されています。

feature  importances、permutation  importances、Boruta、SHAP、PPS、sklearn.feature_selection（scikit-learnライブラリ）などです。

決定木モデルでは、よくfeature importances が利用されていますが、結果にバイアスが生じることもありますので、特徴量をランダムに振った時に目的変数に与える影響を考慮した permutation importances の利用が推奨されていたり、それよりも精度が高いとされる Boruta を推す方もおられたりと、特徴量の選択はすこし混沌としている感があります。

今回は、最近知ったBoruta-Shapというライブラリを使うことにしました。

https://github.com/Ekeany/Boruta-Shap

いくつかのデータセットで試してみた結果、特徴量を絞ったのにスコア向上、もしくはスコア変わらずという状況でした。

良好な結果が得られたこともありますが、グラフ表示がわかりやすいこと、結果を簡単にデータフレームに取り出せることもよい点と感じました。
![boruta.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/65ed576d-af7b-2ddf-4f32-599ce81fbdbf.jpeg)



以上のことから、特徴量選択はBoruta-Shapの使用を前提に以下の方針で進めることにしました。

>- 特徴量の選択前後でランダムフォレストによるスコア比較を行う。
>- 回帰データか分類データかを自動判別して処理を行う。
>- 分析データは、アップロードするデータだけではなく、はじめての人でも感覚が掴めるように、Boruta-Shap とdetaprepのデータセット（回帰はボストン住宅価格、タイタニック、分類は乳がん、アイリス等）も選択できるようにする。




## 特徴量と目的変数の関係性

特徴量と目的変数の関係性は決定木で確認したいと思います。

決定木は、以下の方針で進めることにしました。

- dtreevizを使用する。
- 回帰データでも分類データでも処理できるようにする。
- 決定木は階層が深いと可読しにくいので、木の深さを任意に変えられるようにする。
- 木の深さ等のパラメータの最適解を求めた決定木も描画する。

https://github.com/parrt/dtreeviz

以下は、titanicのDatasetで実行した結果です。
先の処理で特徴量を絞っている（10カラム ⇒ 6カラムに）こと、決定木の深さを浅く（深さ「3」）していることで、全体が掴みやすくなりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/4b4858da-b943-4dd7-5173-d40749efe711.png)

以下は、GridSearchでパラメータを最適化した時の決定木です。私は最適解をよりも可読性を重視しがちですが、見比べるようにしています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/4d916080-cf71-c01f-a70e-0f16841ba14b.png)


## サマリーのアウトプット

最後に、一連の結果をダッシュボードにまとめます。（これはしてみたかった、というものなのでおまけです）
ダッシュボードは、以下の方針で進めることにしました。

>- Plotly／Dash を使用する。
>- 元データ、特徴量選択後のデータフレームを表示する。
>- BorutaSHAPによる feature importance（特徴量重要度）をPlotlyで表示する。
>- 特徴量選択前後のスコアを表示する。
>- dtreevizの決定木をPlotlyで表示する。
>- これらをレポートっぽく、Dashで表示する。

![E07B20DA-A53A-43CD-875B-1C1D5974801D.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/31d343f5-4fe7-b742-20fa-05d3d90dcffc.jpeg)
![598C383E-B248-4C55-8C8F-49327A32CE44.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/672f0521-8ba5-b71a-30d8-7d864c76f288.jpeg)

**↓↓↓ ダッシュボードの表示はこんな感じです。 ↓↓↓**

https://youtu.be/JbByN0dFZU4

## 最後に

- なんとなく、「できたらいいのになぁ」と思っていたことは、なんだかんだで、一応かたちになりましたが、前処理の処理、順序、dtreevizのPlotly表示。。。試行錯誤と失敗の連続でした。もっとこのようにすればいいのにということがあれば、ぜひご教示をお願いします。
- 機械学習のライブラリは「分類用」と「回帰用」で実行ライブラリが異なることがほとんどと思います。いちいち変更するのは面倒なので、Google colabのフォームで、classification か regression かを選ぶ枠を設け、これをキーに読むライブラリを変更しました。これは楽です。
- 実行パターンをいろいろ想定し、準備するも「これはたまにしか動かさなくていいな…」という内容が出てきます。例えば、グラフの画像出力とか、前処理後のデータフレームをcsv出力するとかです。このような場合、Google Colabフォームで☑ bool型（True/False）の枠を設けておくと、実行したい時だけ実行できるから便利です。
- Dashを実装してから、ライブラリのインポート **from jupyter_dash import JupyterDash** でエラーとなることがありました。再実行すると正常に実行されるので何とも言えません。ライブラリ同士（おそらくDashとdataprep）がぶつかっているのかな？と思い、ライブラリ毎にインストール＆インポートを実行させるようにしました。この症状は必ず再現ということでもないので何とも言えませんが、すこし、安定したように思います。Dash実装Ver.のみ、仮にこれが生じたら「ランタイムを再起動」しています。これでも解消します。
 
## 参考

https://qiita.com/virusyun/items/fb06bd8cbbab0862a1ce

https://zenn.dev/nekoallergy/articles/scikit-learn-datasets

https://biotech-lab.org/articles/14290

https://qiita.com/hima2b4/items/e08a9ffd93645c2ffeeb

https://scrapbox.io/PythonOsaka/Python_チュートリアル：Pythonによる探査的データ分析

https://qiita.com/hima2b4/items/1fec0998dd0b206b4a29

https://qiita.com/hima2b4/items/2c742c766ecbd5ba7ab1

https://qiita.com/hima2b4/items/f24a472fcb56bece22e0

https://qiita.com/hima2b4/items/da29530c1f58c7211d44

https://qiita.com/hima2b4/items/65129b3c3c4a1090afae