Pythonでデータの挙動を見やすくする可視化ツールを作成してみた（分類編）

# 概要
データに基づき、自動でラベルを見分ける機械学習手法を**「分類」**と呼びます。
例えば、「大きさ」と「色」のデータから、「リンゴ」と「ミカン」を見分けるようなイメージです。

今回はPythonのグラフ描画ライブラリ「seaborn」をベースにして、
**分類結果の解釈を強力にサポート**するツールを作成しました！

## 機能1. 決定境界の可視化
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/7f73396c-8001-d238-f844-7066193c5562.png)

## 機能2. クラス確率の可視化
**RGB画像表示** (proba_type='imshow')
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/43640b02-ba88-0e67-ad09-911c9ab43c8a.png)

**等高線表示** (proba_type='contourf')
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/6278aeaa-5bdb-91a8-bf6b-add90e316203.png)

## 2021/7 修正：pipでインストールできるよう改良しました
下記コマンドでインストール可能となりました

```
$ pip install seaborn-analyzer
```
**※[関連ツールを1つの記事にまとめました](https://qiita.com/c60evaporator/items/8cb774e65d4423fd93ee)！よければこちらもご覧ください**
![image](https://user-images.githubusercontent.com/59557625/126887193-ceba9bdd-3653-4d58-a916-21dcfe9c38a0.png)
もしこのツールを良いと思われたら、**[GitHubにStar頂けるとありがたいです！](https://github.com/c60evaporator/seaborn_analyzer)**

# 背景
分類は、データの基づき自動でラベル（「クラス」「目的変数」とも呼ぶ）を見分ける手法です
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/af563947-7569-b4f8-b157-a1ecd313d6ac.png)
分類の手法として、画像や音声分野ではディープラーニングが主流となりつつありますが、
その他の分野では、解釈性の高さから**特徴量を利用した機械学習**が依然として多く使われています。

## 特徴量と可視化
**「特徴量」**とは、先ほどの果物データの例で挙げた「大きさ」「色」など、ものの特徴を表す数値データを表し、「説明変数」とも呼ばれます。

例えば、以下のように、「大きさ」「色(赤さ)」の特徴量に対し、リンゴとミカンを散布図（「特徴量空間」と呼ぶ）プロットすると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/e613bbdf-6c01-b5d8-4fba-b1ab6c2b83fe.png)
**・経験則との比較**
→「リンゴの方がミカンより大きくて赤い」という経験と、特徴量空間上での位置関係が一致している

**・未知のデータに対する判定予測**
→リンゴとミカンの判定の境界（**決定境界**）特徴量空間上で可視化できれば、未知のデータ（例：青リンゴ）がどちらのクラスと判定されるか分かる

といった具合に、**特徴量による可視化が結果解釈に貢献**する事が分かります。
分析結果を他者に説明する際も、この解釈性の高さは助けとなるでしょう。


# ユースケース
上記のように、特徴量による分類は解釈性の高さに魅力がありますが、
**グラフでの描画**ができないと、この解釈性の高さが半減してしまいます。

そのため本ツールでは
**「描画できると嬉しいが実装するのが大変なグラフ」を、
簡単に描画できるようライブラリ化しました。**

機械学習の定番であるiris（アヤメ）データセットで、ユースケースを見ていきます

## ユースケース1：決定境界の可視化

上司「3種類のアヤメ"setosa", "versicolor", "virginica"を分類するモデルを作ってくれ」

irisデータセットには、4種類の特徴量が含まれていますが、このうち2種類（"petal_width", "petal_length"）を使って可視化してみます

```python
import seaborn as sns
iris = sns.load_dataset("iris")
sns.scatterplot(x='petal_width', y='petal_length', data=iris,
                hue='species', palette=['green', 'red', 'blue'])
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/a1517ae3-9f78-aa6e-451e-dd5fd5ff0bc1.png)

ぼく「定番のサポートベクターマシン（SVM）を使って分類してみよう」

```python
# %% サポートベクターマシン分類して性能評価指標算出
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score, f1_score
X = iris[['petal_width', 'petal_length']].values  # 説明変数
y = iris['species'].values  # 目的変数(3種類のアヤメ種類)
clf = SVC()  # SVM分類用インスタンス
clf.fit(X, y)  # 学習
y_pred = clf.predict(X)  # 推論
print(f'Accuracy={accuracy_score(y, y_pred)}')  # 正解率を表示
print(f'F1={f1_score(y, y_pred, average="macro")}')  # F1-Macroを表示
```

```
> Accuracy=0.9533333333333334
> F1=0.9532912954992826
```

ぼく「SVMで分類したところ、性能を表す正解率が0.95、F1-Scoreは0.95と良好です」

上司「数値だけじゃ分かりにくいなあー！グラフで見やすくしてくれ！」

ぼく「どんなグラフにすればいいですか？」

上司「それを考えるのがキミの仕事だろう」

ぼく「ぴえん🥺」

### ユースケース1の解決策
このようなとき、クラス判定の境目である決定境界をグラフ表示できると、上司も納得の可視化が実現できます。
決定境界の描画には、[mlxtendというライブラリ](https://qiita.com/altescy/items/60a6def66f13267f6347#plot_decision_regions)が便利です

```python:mlxtendで決定境界可視化
from mlxtend.plotting import plot_decision_regions
import numpy as np
# 目的変数の配列を2次元に変換
y = y.reshape(len(y), 1)
# 目的変数をstr型→int型に変換
label_names = list(dict.fromkeys(y[:, 0]))
label_dict = dict(zip(label_names, range(len(label_names))))
y_int=np.vectorize(lambda x: label_dict[x])(y)
# 学習
clf.fit(X, y_int)
# mlxtendで決定境界可視化
plot_decision_regions(X, y_int[:, 0], clf=clf,
                      colors='green,red,blue')
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/17268089-3dbb-8098-f75a-97377aa906ba.png)
しかしこのmlxtend、目的変数（ラベルなので通常はstr型）をint型に変換しなければならず、入力データ形式（元々はPandasのDataFrameが多い）も2次元のndarrayしか受け付けないため、前処理に手間が掛かります

### 本ツールによる可視化方法
入力データ形式をPandasのDataFrame、目的変数がstr型（int型もOK）と、
オーソドックスな形式で簡単に決定境界を描画できる、

**classplot.class_separator_plot()**

というメソッドを作成しました。

他の改善点として、正解ラベル(True)と不正解ラベル(False)の判別や、軸ラベル描画機能も追加しています。

```python
from seaborn_analyzer import classplot
clf= SVC()
classplot.class_separator_plot(clf, ['petal_width', 'petal_length'], 'species', iris)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/7f73396c-8001-d238-f844-7066193c5562.png)
SVM以外でも、**Scikit-learn APIに対応した分類モデルであれば適用可能です**

```python:ランダムフォレストでの描画例
from sklearn.ensemble import RandomForestClassifier
clf= RandomForestClassifier()
classplot.class_separator_plot(clf, ['petal_width', 'petal_length'], 'species', iris)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/df4f44ff-6371-0352-2d38-b37b0919aea3.png)
また、評価データと学習データを分けて評価したい方向けに、
クロスバリデーションでの描画にも対応しています。
※クロスバリデーションの指定法は後述します

```python:2分割クロスバリデーション
classplot.class_separator_plot(clf, ['petal_width', 'petal_length'], 'species', iris,
                               cv=2, display_cv_indices = [0, 1])
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/a942051c-a70c-5bf1-e67b-87d85614b79b.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/4afb9e10-03c4-31a5-597b-6584eebf66ae.png)

3次元特徴量の場合、3個目を一定間隔で分割し、分割された区間の中央値を使用した予測値で決定境界を、分割された区間内のデータで散布図をプロットします
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/6c38907a-3616-de34-564d-f11a5b532c0a.png)
4次元特徴量の場合も、縦横に分割してプロット可能です。プロットに時間が掛かるのでご注意ください
（chart_scale引数を2以上にすれば、解像度を落としてプロット時間を早くできます）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/b1621200-49b4-47af-f0bc-e00de9998e8f.png)


## ユースケース2：クラス確率の可視化
ぼく「アヤメ分類の決定境界を可視化したグラフがこちらです」

上司「こういう1か0かの極端なグラフだと、判定の信頼性が分からんな。どの種類のアヤメである確率が何パーセント、みたいな可視化はできないの？」

ぼく「（この人理想が高いな‥）難しいかと思います」

上司「やる前からできないと言うのは熱意が足りないんじゃないかい？」

ぼく「ぴえん🥺」

上記のように**どのクラスに属する確率（以下「クラス確率」と呼びます）**が何パーセント、という情報が得られば、
分類の確信度として利用する事で、**より定量的な判断**ができます。

### ユースケース2の解決策
幸いなことに、Scikit-Learnの分類モデルには、クラス確率を計算する`predict_proba()`というメソッドが実装されています。
predict_probaメソッドで求められる確率は、[ロジスティク関数を使用した擬似的なもの](https://qiita.com/thtitech/items/4c0777f3825572d7e0eb)のようですが、今まで使用した限り、目安としてして使用するには十分な性能があると感じています。

Scikit-Learn公式にも、Classifier Comparisonという可視化のサンプルコードが記載されていますが、2クラスのみの対応です。
![image.png](https://scikit-learn.org/stable/_images/sphx_glr_plot_classifier_comparison_001.png)

### 本ツールによる可視化方法
`predict_proba()`で算出したクラス確率を可視化するため、

**classplot.class_proba_plot()**

というメソッドを作成しました。
等高線表示（塗りつぶし選択可）、あるいはRGB画像表示の3種類の方法でグラフを描画できます
（RGB画像表示は[こちら](https://www.haya-programming.com/entry/2019/06/22/235133)を参考にさせて頂きました）

```python:等高線表示（塗りつぶしあり）
clf = SVC(probability=True)  # SVMでpredict_probaを有効にするため、引数`probability`をTrueに
classplot.class_proba_plot(clf, ['petal_width', 'petal_length'], 'species', iris,
                           proba_type='contourf')
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/6278aeaa-5bdb-91a8-bf6b-add90e316203.png)

```python:等高線表示（塗りつぶしなし）
clf = SVC(probability=True)  # SVMでpredict_probaを有効にするため、引数`probability`をTrueに
classplot.class_proba_plot(clf, ['petal_width', 'petal_length'], 'species', iris,
                           proba_type='contour')
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/0c526d82-e6ed-a4ef-350b-6cb82fba1f75.png)

```python:RGB画像表示
clf = SVC(probability=True)  # SVMでpredict_probaを有効にするため、引数`probability`をTrueに
classplot.class_proba_plot(clf, ['petal_width', 'petal_length'], 'species', iris,
                           proba_type='imshow')
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/43640b02-ba88-0e67-ad09-911c9ab43c8a.png)
それぞれのメリット、デメリットは下記となります

||等高線表示|RGB画像表示|
|---|---|---|
|メリット|4クラス以上でも使用可|表示が連続的で見やすい|
|デメリット|等高線が重なって見辛いこともある|3クラス以下でしか使用できない|

class_separator_plotと同様に、クロスバリデーションや3～4次元でのプロットにも対応しています。

# 使用に必要なもの
・Python本体 (動作確認時は3.9.4を使用)
・Matplotlibで表示可能な環境 (Jupyter等)
・下記ライブラリ (参考として動作確認時のライブラリバージョンも記載)
　seaborn (0.11.1)
　numpy (1.20.1)
　pandas (1.2.2)
　matplotlib (3.3.4)
　scipy (1.6.0)
　scikit-learn (0.24.1)

# インストール方法
下記コマンドでインストール可能です

```
$ pip install seaborn-analyzer
```
※アンダースコアの`pip install seaborn_analyzer`でもインストール可能です。
インポート時はアンダースコアの`import seaborn_analyzer`や`from seaborn_analyzer`
となるのでご注意ください

# コード
モジュールcustom_scatter_plot.py内のクラス`classplot`に、必要な処理をまとめました。
seabornのscatterplotぽいAPIで使用できるようにするため、クラスメソッドを利用しています。

・[GitHubの該当部分ソースコードはこちら](https://github.com/c60evaporator/seaborn_analyzer/blob/master/custom_scatter_plot.py#L1206)

# 各メソッドの解説
実行可能なメソッドは下記2個となります

|メソッド名|機能|
|---|---|
|class_separator_plot|ユースケース1で紹介した、決定境界プロット|
|class_proba_plot|ユースケース2で紹介した、クラス確率プロット|

## 使用方法
### class_separator_plotメソッド（決定境界）
実行元スクリプトと同フォルダにcustom_scatter_plot.pyを置き、下記のように実行します

```python
# 参考としてirisデータを読み込んでいます
import seaborn as sns
iris = sns.load_dataset("iris")
# 分類モデルとしてサポートベクターマシンを使用
from sklearn.svm import SVC
clf = SVC()
# ここから実行部分（引数clf, x, y, dataの指定はMUST）
from seaborn_analyzer import classplot
classplot.class_separator_plot(clf, x=['petal_width', 'petal_length'], y='species', data=iris)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/bd01bd6d-0930-191f-40e5-018b9390a860.png)

### class_proba_plotメソッド（クラス確率）
実行元スクリプトと同フォルダにcustom_scatter_plot.pyを置き、下記のように実行します

```python
# 参考としてirisデータを読み込んでいます
import seaborn as sns
iris = sns.load_dataset("iris")
# 分類モデルとしてサポートベクターマシンを使用(引数probability=True指定が必要)
from sklearn.svm import SVC
clf = SVC(probability=True)
# ここから実行部分（引数clf, x, y, dataの指定はMUST）
from seaborn_analyzer import classplot
classplot.class_proba_plot(clf, x=['petal_width', 'petal_length'], y='species', data=iris)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/7f9e8378-ab49-2099-e810-956e8dcc3b17.png)
なお、SVM（SVC）のような一部のアルゴリズムでは、predict_probaの計算を有効にするために、引数`probablity`=Trueを指定する必要があることにご注意ください。
（predict_probaは乱数を利用しているので、毎回結果が変わるのが嫌な方は`random_seed`引数も指定してください）

# 引数の解説
メソッドごとに引数を解説します

## オプション引数を指定しないとき
### class_separator_plotメソッド（決定境界図）
下記の値が自動入力されます（clf, x, y, dataは入力必須）
(clf, x, y, data, x_chart = None,
pair_sigmarange = 1.5, pair_sigmainterval = 0.5, chart_extendsigma = 0.5, chart_scale = 1,
plot_scatter = 'class_error', rounddigit_x3 = 2,
scatter_colors = None, true_marker = 'o', false_marker = 'x',
cv=None, cv_seed=42, cv_group=None, display_cv_indices = 0,
clf_params=None, fit_params=None, subplot_kws=None, contourf_kws=None, scatter_kws=None)

**・表示例**
irisデータセット＋サポートベクターマシンでの実行例

```python
from seaborn_analyzer import classplot
clf = SVC()
classplot.class_separator_plot(clf, x=['petal_width', 'petal_length'], y='species', data=iris)
```
実行すると下図のようなグラフ(決定境界図)が表示されます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/bd01bd6d-0930-191f-40e5-018b9390a860.png)

### class_proba_plotメソッド（クラス確率図）
下記の値が自動入力されます（clf, x, y, dataは入力必須）
(clf, x, y, data, x_chart = None,
pair_sigmarange = 1.5, pair_sigmainterval = 0.5, chart_extendsigma = 0.5, chart_scale = 1,
plot_border = True, plot_scatter = 'class', rounddigit_x3 = 2,
proba_class = None, proba_cmap_dict = None, proba_type = 'contourf',
scatter_colors = None, true_marker = 'o', false_marker = 'x',
cv=None, cv_seed=42, cv_group=None, display_cv_indices = 0,
clf_params=None, fit_params=None, subplot_kws=None, contourf_kws=None, imshow_kws=None, scatter_kws=None)

**・表示例**
irisデータセット＋サポートベクターマシンでの実行例

```python
from seaborn_analyzer import classplot
clf = SVC()
classplot.class_proba_plot(clf, x=['petal_width', 'petal_length'], y='species', data=iris)
```
実行すると下図のようなグラフ(等高線表示でのクラス確率図)が表示されます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/7f9e8378-ab49-2099-e810-956e8dcc3b17.png)

## class_separator_plotメソッド、class_proba_plotメソッド共通引数

|引数名|必須引数orオプション|型|デフォルト値|内容|
|---|---|---|---|---|
|clf|必須|Scikit-learn API|-|表示対象の分類モデル|
|x|必須|List[str]|-|説明変数に指定するカラム名|
|y|必須|str|-|目的変数に指定するカラム名|
|data|必須|pd.DataFrame|-|入力するデータ<br>(Pandasのデータフレーム)|
|x_chart|オプション|List[str]|None|説明変数のうちグラフ表示対象のカラム名|
|pair_sigmarange|オプション|float|1.5|グラフ非使用変数の分割範囲|
|pair_sigmainterval|オプション|float|0.5|グラフ非使用変数の1枚あたり表示範囲|
|chart_extendsigma|オプション|float|0.5|グラフ縦軸横軸の表示拡張範囲|
|chart_scale|オプション|int|1|グラフの描画倍率|
|plot_scatter|オプション|str|'true'|散布図の描画種類|
|rounddigit_x3|オプション|int|2|グラフ非使用軸の小数丸め桁数|
|scatter_colors|オプション|List[str]|None|クラスごとのプロット色のリスト|
|true_marker|オプション|str|None|正解クラスの散布図プロット形状|
|false_marker|オプション|str|None|不正解クラスの散布図プロット形状|
|cv|オプション|int or sklearn.model_selection.* |None|クロスバリデーション分割法|
|cv_seed|オプション|int|42|クロスバリデーションの乱数シード|
|cv_group|オプション|str|None|GroupKFold,LeaveOneGroupOut等のグルーピング対象カラム名|
|display_cv_indices|オプション|int|0|表示対象のクロスバリデーション番号|
|clf_params|オプション|dict|None|分類モデルに渡すパラメータ|
|fit_params|オプション|dict|None|学習時のパラメータ|
|subplot_kws|オプション|dict|None|matplotlib.pyplot.subplotsに渡す引数|
|contourf_kws|オプション|dict|None|グラフ表示用のmatplotlib.pyplot.contourf()に渡す引数|
|scatter_kws|オプション|dict|None|散布図用のmatplotlib.pyplot.scatter()に渡す引数|
オプション引数の詳細は後述します

### x_chart
**3説明変数以上のときのみ有効**なのでご注意ください
説明変数のうち、グラフの横軸と縦軸のカラム名を指定します。
指定しなかった説明変数を使い、
「pair_sigmarange引数 × 標準偏差」の範囲内で、
「pair_sigmainterval引数 × 標準偏差」ごとに一定間隔で図を分割します。
※Noneを指定すると、引数`x`で指定した説明変数の前から2個を自動指定します。

```python:決定境界のとき(class_separator_plot)
classplot.class_separator_plot(clf, x=['petal_width', 'petal_length', 'sepal_width'],
                               y='species', data=iris,
                               x_chart=['petal_width', 'petal_length'],
                               pair_sigmarange = 1.0, pair_sigmainterval = 0.5)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/6c38907a-3616-de34-564d-f11a5b532c0a.png)

```python:クラス確率のとき(class_proba_plot)
classplot.class_proba_plot(clf, x=['petal_width', 'petal_length', 'sepal_width'],
                           y='species', data=iris,
                           x_chart=['petal_width', 'petal_length'],
                           pair_sigmarange = 1.0, pair_sigmainterval = 0.5)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/bd08d5fe-e134-f225-8330-f7f7216465b5.png)

コツとしては、グラフ"内"で決定境界・クラス確率の変化が追いやすいよう、
**目的変数に対する影響度が大きい説明変数を指定**することです。
（影響度の大きさは、[後述の「特徴量重要度」](https://qiita.com/c60evaporator/items/43866a42e09daebb5cc0#%E7%89%B9%E5%BE%B4%E9%87%8F%E9%87%8D%E8%A6%81%E5%BA%A6feature-importance)などで算出可能）

影響度の小さい説明変数を指定すると、グラフ"間"で決定境界やクラス確率が変化してしまい、
1枚のグラフ内で変化が追いにくくなってしまいます。

### pair_sigmarange
**3説明変数以上のときのみ有効**なのでご注意ください
グラフ非使用変数（x_chartで未指定の説明変数）での図の分割範囲を指定します。
「pair_sigmarange引数 × 標準偏差」の範囲内で、
「pair_sigmainterval引数 × 標準偏差」ごとに一定間隔で図を分割します。

```python:決定境界のとき(class_separator_plot)
classplot.class_separator_plot(clf, x=['petal_width', 'petal_length', 'sepal_width'],
                               y='species', data=iris,
                               x_chart=['petal_width', 'petal_length'],
                               pair_sigmarange = 0.5, pair_sigmainterval = 0.5)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/22b0a631-e05d-af70-9426-20875d6de0e2.png)

```python:クラス確率のとき(class_proba_plot)
classplot.class_proba_plot(clf, x=['petal_width', 'petal_length', 'sepal_width'],
                           y='species', data=iris,
                           x_chart=['petal_width', 'petal_length'],
                           pair_sigmarange = 0.5, pair_sigmainterval = 0.5)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/dacec84b-ead2-6176-ffcd-15e18fecab7d.png)

なお、表示される決定境界図あるいはクラス確率図における**グラフ非使用変数の値**は、
・pair_sigmarange範囲内の図：pair_sigmaintervalの中央値
・pair_sigmarange範囲外の図(最初の図)：-pair_sigmarange - pair_sigmainterval/2
・pair_sigmarange範囲外の図(最後の図)：pair_sigmarange + pair_sigmainterval/2
を使用します

### pair_sigmainterval
**3説明変数以上のときのみ有効**なのでご注意ください
グラフ非使用変数（x_chartで未指定の説明変数）での図の分割範囲を指定します。
「pair_sigmarange引数 × 標準偏差」の範囲内で、
「pair_sigmainterval引数 × 標準偏差」ごとに一定間隔で図を分割します。

```python:決定境界のとき(class_separator_plot)
classplot.class_separator_plot(clf, x=['petal_width', 'petal_length', 'sepal_width'],
                               y='species', data=iris,
                               x_chart=['petal_width', 'petal_length'],
                               pair_sigmarange = 0.5, pair_sigmainterval = 0.25)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/b19c3ff1-baa4-fcf2-b20c-7ac34e33f211.png)

```python:クラス確率のとき(class_proba_plot)
classplot.class_proba_plot(clf, x=['petal_width', 'petal_length', 'sepal_width'],
                           y='species', data=iris,
                           x_chart=['petal_width', 'petal_length'],
                           pair_sigmarange = 0.5, pair_sigmainterval = 0.25)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/67333a22-b36d-e852-a54b-3ad153b9e896.png)

### chart_extendsigma
グラフ縦軸横軸の表示拡張範囲を指定します。
散布図データの最大最小値に対し、「chart_extendsigma × 標準偏差」だけ縦軸横軸範囲を拡張します

```python:決定境界のとき(class_separator_plot)
classplot.class_separator_plot(clf, x=['petal_width', 'petal_length'],
                               y='species', data=iris,
                               chart_extendsigma=3.0)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/3e37c914-cf27-8e8a-c805-96258e4b8412.png)

```python:クラス確率のとき(class_proba_plot)
classplot.class_proba_plot(clf, x=['petal_width', 'petal_length'],
                           y='species', data=iris,
                           chart_extendsigma=3.0)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/f43c0a22-4338-8292-b639-9b0d4ed42fde.png)
機械学習アルゴリズムは、データがない部分の境界が思わぬ形状をしている事も多いので、
特徴量空間上を範囲を広げて見る際に便利な引数です。

### chart_scale
グラフの解像度倍率を指定します。
**大きくなるほどグラフ表示が粗くなりますが、描画速度が速くなります**
1を指定すると、グラフ全体をドット数（デフォルトでは432x288）で分割してデータ点作成し、
2を指定するとドット数の半分、3を指定すると1/3といった具合で解像度が粗くなります。

```python:決定境界のとき(class_separator_plot)
classplot.class_separator_plot(clf, x=['petal_width', 'petal_length'],
                               y='species', data=iris,
                               chart_scale=10)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/06246dcf-7782-cfbd-7a49-35d30e180ee4.png)

```python:クラス確率のとき(class_proba_plot)
classplot.class_proba_plot(clf, x=['petal_width', 'petal_length'],
                           y='species', data=iris,
                           chart_scale=20)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/43d84f79-6bc6-f85b-1ce8-f513f0993cab.png)
4を超えるとかなりジグザグな描画となってしまうため、3以下に抑えた方がよいかと思います。

### plot_scatter
散布図の色分け・形状分け方法を選択します。下表から選択できます

|plot_scatterの指定値|散布図の色分け・形状分け方法|
|---|---|
|'error'|正誤で形状分け|
|'class'|クラスで色分け|
|'class_error'|正誤で形状分け＆クラスで色分け|
|None|散布図表示なし|

#### plot_scatter='error'のとき
正誤（予測クラス=実際のクラスのときTrue、そうでないときFalse）で散布図を色＆形状分けします

```python:決定境界のとき(class_separator_plot)
classplot.class_separator_plot(clf, x=['petal_width', 'petal_length'],
                               y='species', data=iris,
                               plot_scatter='error')
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/8d223a20-89c7-3f15-4224-a1dbacf6326c.png)

```python:クラス確率のとき(class_proba_plot)
classplot.class_proba_plot(clf, x=['petal_width', 'petal_length'],
                           y='species', data=iris,
                           plot_scatter='error')
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/c0fc5249-aceb-cd26-fe3b-7546e8c0283a.png)

#### plot_scatter='class'のとき
実際のクラスで散布図を色分けします（class_proba_plotのデフォルト設定）

```python:決定境界のとき(class_separator_plot)
classplot.class_separator_plot(clf, x=['petal_width', 'petal_length'],
                               y='species', data=iris,
                               plot_scatter='class')
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/4a1697b7-808e-94ef-fe30-b0a725b3879b.png)

```python:クラス確率のとき(class_proba_plot)
classplot.class_proba_plot(clf, x=['petal_width', 'petal_length'],
                           y='species', data=iris,
                           plot_scatter='class')
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/a7a535de-9939-fe57-8df1-aa6b83874a89.png)

#### plot_scatter='class_error'のとき
正誤で形状分け、実際のクラスで色分けします（class_separator_plotのデフォルト設定）

```python:決定境界のとき(class_separator_plot)
classplot.class_separator_plot(clf, x=['petal_width', 'petal_length'],
                               y='species', data=iris,
                               plot_scatter='class')
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/fbf12c59-9874-c746-467a-29d346947e62.png)

```python:クラス確率のとき(class_proba_plot)
classplot.class_proba_plot(clf, x=['petal_width', 'petal_length'],
                           y='species', data=iris,
                           plot_scatter='class')
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/4ba538c4-466f-5caa-b1a5-85bc233ce7f6.png)

#### plot_scatter=Noneのとき
散布図をプロットしません

```python:決定境界のとき(class_separator_plot)
classplot.class_separator_plot(clf, x=['petal_width', 'petal_length'],
                               y='species', data=iris,
                               plot_scatter=None)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/7b3d6790-f625-67e9-70d3-0af0a29cd138.png)

```python:クラス確率のとき(class_proba_plot)
classplot.class_proba_plot(clf, x=['petal_width', 'petal_length'],
                           y='species', data=iris,
                           plot_scatter=None)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/eeab24e6-f417-9a9c-e8e6-42da6c3022d3.png)

### rounddigit_x3
**3説明変数以上のときのみ有効**なのでご注意ください
グラフ非表示変数の小数丸め桁数の小数丸め桁数を指定します
（各グラフの上に表示される非表示変数の範囲表示の桁数です）

```python:決定境界のとき(class_separator_plot)
classplot.class_separator_plot(clf, x=['petal_width', 'petal_length', 'sepal_width'],
                               y='species', data=iris,
                               x_chart=['petal_width', 'petal_length'],
                               pair_sigmarange=0.5, pair_sigmainterval=0.5,
                               rounddigit_x3=4)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/415d72ac-9dcf-650d-65c8-cd289b2315ca.png)

```python:クラス確率のとき(class_proba_plot)
classplot.class_proba_plot(clf, x=['petal_width', 'petal_length', 'sepal_width'],
                           y='species', data=iris,
                           x_chart=['petal_width', 'petal_length'],
                           pair_sigmarange=0.5, pair_sigmainterval=0.5)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/9f3ceb0c-51ab-05c7-ce41-6d97d7c2c080.png)

### scatter_colors
クラスごとの**散布図プロット色をリストで指定**します。

**class_separator_plotメソッド**では、**決定境界の塗りつぶし色**もこの指定に準拠します。
**class_proba_plotメソッドでのクラス確率塗りつぶし色**指定は、**別途proba_cmap_dict引数**で指定する必要があるので注意してください。
また、クラスの数とリストの要素数が一致する必要があります。

指定できる色の名称は[matplotlibに準拠します](https://pythondatascience.plavox.info/matplotlib/%E8%89%B2%E3%81%AE%E5%90%8D%E5%89%8D)

デフォルトでは、緑('green') → 赤('red') → 青('blue') → 茶('brown')‥の順で指定されています。

```python:決定境界のとき(class_separator_plot)
classplot.class_separator_plot(clf, x=['petal_width', 'petal_length'],
                               y='species', data=iris,
                               scatter_colors=['yellow', 'magenta', 'cyan'])
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/bce85902-9f08-dea2-84fb-90e6a0744513.png)

```python:クラス確率のとき(class_proba_plot)
classplot.class_proba_plot(clf, x=['petal_width', 'petal_length'],
                           y='species', data=iris,
                           scatter_colors=['yellow', 'magenta', 'cyan'])
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/4512cc76-5d07-18e0-6d42-831dbf1c07ba.png)

### true_marker
`plot_scatter`引数に"error"または"class_error"を指定しているとき、
本引数で正解データ（予測ラベル=実際のラベル）の散布図描画形状を指定します。

指定できるマーカーの名称は[matplotlibに準拠します](https://pythondatascience.plavox.info/matplotlib/%E3%83%9E%E3%83%BC%E3%82%AB%E3%83%BC%E3%81%AE%E5%90%8D%E5%89%8D)

```python:決定境界のとき(class_separator_plot)
classplot.class_separator_plot(clf, x=['petal_width', 'petal_length'],
                               y='species', data=iris,
                               true_marker='s')
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/d5147b52-749f-21d5-5e43-724de024656c.png)

```python:クラス確率のとき(class_proba_plot)
classplot.class_proba_plot(clf, x=['petal_width', 'petal_length'],
                           y='species', data=iris,
                           plot_scatter='class_error',
                           true_marker='s')
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/2c981c92-f062-156e-6aed-0b605ac9aa9f.png)

### false_marker
`plot_scatter`引数に"error"または"class_error"を指定しているとき、
本引数で不正解データ（予測ラベル≠実際のラベル）の散布図描画形状を指定します。

指定できるマーカーの名称は[matplotlibに準拠します](https://pythondatascience.plavox.info/matplotlib/%E3%83%9E%E3%83%BC%E3%82%AB%E3%83%BC%E3%81%AE%E5%90%8D%E5%89%8D)

```python:決定境界のとき(class_separator_plot)
classplot.class_separator_plot(clf, x=['petal_width', 'petal_length'],
                               y='species', data=iris,
                               false_marker='^')
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/edf069bb-d72e-0fdb-c5c0-3f874335ed18.png)

```python:クラス確率のとき(class_proba_plot)
classplot.class_proba_plot(clf, x=['petal_width', 'petal_length'],
                           y='species', data=iris,
                           plot_scatter='class_error',
                           false_marker='^')
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/1b638bb0-18b9-4b34-7ba3-9c65e285aceb.png)

### cv
クロスバリデーション分割法を指定します。

数値を指定すれば、指定した数に応じて通常のK-Foldで分割します。
デフォルトではクロスバリデーションの最初のセクション（CV_Number=0）の結果のみがプロットされます。

```python:決定境界のとき(class_separator_plot)
classplot.class_separator_plot(clf, x=['petal_width', 'petal_length'],
                               y='species', data=iris,
                               cv=2)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/2205c0f9-f42d-308d-dc18-aa02cb068d4e.png)

```python:クラス確率のとき(class_proba_plot)
classplot.class_proba_plot(clf, x=['petal_width', 'petal_length'],
                           y='species', data=iris,
                           cv=2)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/2252d06c-be18-c560-6b14-cdce1b3f4c20.png)

全てのセクションの結果をプロットしたい場合、引数`display_cv_indices`でCV_Numberをリスト指定します

```python:決定境界のとき(class_separator_plot)
classplot.class_separator_plot(clf, x=['petal_width', 'petal_length'],
                               y='species', data=iris,
                               cv=2, display_cv_indices = [0, 1])
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/2205c0f9-f42d-308d-dc18-aa02cb068d4e.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/f7de20de-e9a8-b689-8211-dd8a8d2e59c3.png)

```python:クラス確率のとき(class_proba_plot)
classplot.class_proba_plot(clf, x=['petal_width', 'petal_length'],
                           y='species', data=iris,
                           cv=2, display_cv_indices = [0, 1])
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/2252d06c-be18-c560-6b14-cdce1b3f4c20.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/ab762855-1a0c-a006-369c-9df6331b0421.png)

Scikit-LearnのAPIに対応していれば、ShuffleSplit、GroupKFoldなどの特殊な分割法にも対応しています。

```python:決定境界のとき(class_separator_plot)
from sklearn.model_selection import ShuffleSplit
classplot.class_separator_plot(clf, x=['petal_width', 'petal_length'],
                               y='species', data=iris,
                               cv=ShuffleSplit(n_splits=2, test_size=0.1))
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/a2d4844d-8fe6-4704-61ec-8f48e54b42bd.png)

```python:クラス確率のとき(class_proba_plot)
from sklearn.model_selection import ShuffleSplit
classplot.class_proba_plot(clf, x=['petal_width', 'petal_length'],
                           y='species', data=iris,
                           cv=ShuffleSplit(n_splits=2, test_size=0.1))
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/8aeb0f0f-a425-26e2-b899-30c3e5167508.png)

GroupKFold、LeaveOneGroupOutなどのグルーピング系の分割法では、引数`cv_group`で指定したグループを分割に使用します。

```python:決定境界のとき(class_separator_plot)
from sklearn.model_selection import LeaveOneGroupOut
# グルーピング用にsepal_widthを整数に丸める
iris['sepal_width_int'] = iris['sepal_width'].apply(lambda x: int(x))
classplot.class_separator_plot(clf, x=['petal_width', 'petal_length'],
                               y='species', data=iris,
                               cv=LeaveOneGroupOut(), cv_group='sepal_width_int',
                               display_cv_indices=[0, 1, 2])
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/27d4a602-d4c7-aa3b-91cd-753dc92464c4.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/86064560-ce20-efa5-6b7a-b5fcf6ac7841.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/60854f68-2301-f835-4f64-016f887d7b66.png)

```python:クラス確率のとき(class_proba_plot)
from sklearn.model_selection import LeaveOneGroupOut
# グルーピング用にsepal_widthを整数に丸める
iris['sepal_width_int'] = iris['sepal_width'].apply(lambda x: int(x))
classplot.class_proba_plot(clf, x=['petal_width', 'petal_length'],
                           y='species', data=iris,
                           cv=ShuffleSplit(n_splits=2, test_size=0.1))
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/d0e4a2f4-1367-4a32-8430-a57e3eec1b51.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/65e19116-feb6-acc1-bdbe-97c2df03fc02.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/eba16a59-35a0-f8a2-ea37-4cfd5c6313a7.png)

### cv_seed
引数`cv`で数値指定した際の、クロスバリデーション指定時の乱数シードを指定します
（デフォルトでは乱数シード42を使用しています）

```python:決定境界のとき(class_separator_plot)
classplot.class_separator_plot(clf, x=['petal_width', 'petal_length'],
                               y='species', data=iris,
                               cv=2, cv_seed=43)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/090d694d-db1d-a5b1-871c-0d4da50138ec.png)

```python:クラス確率のとき(class_proba_plot)
classplot.class_proba_plot(clf, x=['petal_width', 'petal_length'],
                           y='species', data=iris,
                           cv=2, cv_seed=43)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/a8de9702-a0bf-0133-421c-429df4459434.png)

### display_cv_indices
クロスバリデーション分割時に、表示するクロスバリデーション番号を指定します。

```python:決定境界のとき(class_separator_plot)
classplot.class_separator_plot(clf, x=['petal_width', 'petal_length'],
                               y='species', data=iris,
                               cv=2, display_cv_indices=1)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/5baa442b-77c2-b7da-0e14-b5e560765579.png)

```python:クラス確率のとき(class_proba_plot)
classplot.class_proba_plot(clf, x=['petal_width', 'petal_length'],
                           y='species', data=iris,
                           cv=2, display_cv_indices=1)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/05d73c12-8e65-3194-1390-3e4ce5078226.png)

[前述のように](https://qiita.com/c60evaporator/items/43866a42e09daebb5cc0#cv)、リスト指定で複数のクロスバリデーション番号でのグラフを同時表示させることも可能です

### clf_params
分類モデルに渡すパラメータをdict指定します。
理想的には、チューニング後のパラメータを渡すのが望ましいです。
（未指定ならば、scikit-learnのデフォルトパラメータを使用します）

下の例では、SVMのパラメータCおよびgammaを大きくして過学習寄りに調整しています。
（[SVMのパラメータはこちらの記事参照](https://qiita.com/c60evaporator/items/8864f7c1384a3c6e9bd9#b-%E9%9D%9E%E7%B7%9A%E5%BD%A2)）

```python:決定境界のとき(class_separator_plot)
classplot.class_separator_plot(clf, x=['petal_width', 'petal_length'],
                               y='species', data=iris,
                               clf_params={'C': 10,
                                           'gamma': 10})
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/6ceefb4a-a7bf-29b8-b449-13efd70bd05c.png)

```python:クラス確率のとき(class_proba_plot)
classplot.class_proba_plot(clf, x=['petal_width', 'petal_length'],
                           y='species', data=iris,
                           clf_params={'C': 10,
                                       'gamma': 10})
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/1166c60f-c548-1e4e-42e1-ce2909721cd1.png)

### fit_params
学習時の`fit`メソッドに渡すパラメータをdict指定します。
XGBoostやLightGBMにおける`early_stoppling_round`, `verbose`パラメータなどを想定しています。

```python:XGBoostでearly_stoppling_round等を指定(class_separator_plot)
from xgboost import XGBClassifier
clf = XGBClassifier()
classplot.class_separator_plot(clf, x=['petal_width', 'petal_length'],
                               y='species', data=iris,
                               cv=2,
                               fit_params={'early_stopping_rounds': 20,
                                           'eval_set': [(iris[['petal_width', 'petal_length']].values, iris['species'].values)],
                                           'verbose': 1})
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/8c57b293-d766-60fd-a197-20a955846570.png)

```python:XGBoostでearly_stoppling_round等を指定(class_proba_plot)
from xgboost import XGBClassifier
clf = XGBClassifier()
classplot.class_proba_plot(clf, x=['petal_width', 'petal_length'],
                           y='species', data=iris,
                           cv=2,
                           fit_params={'early_stopping_rounds': 20,
                                       'eval_set': [(iris[['petal_width', 'petal_length']].values, iris['species'].values)],
                                       'verbose': 1})
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/541150c9-6dce-30ec-3d71-38fafd73e752.png)

### subplot_kws
画像作成用メソッドmatplotlib.pyplot.subplotsに渡す引数をdict指定できます。
渡せる引数は、[こちら](https://qiita.com/nkay/items/d1eb91e33b9d6469ef51#22-pltsubplots)や[こちら](https://qiita.com/nkay/items/d1eb91e33b9d6469ef51#11-pltfigure)をご参照ください

下の例のように、画像のサイズ（'figsize'、デフォルトは画像1枚あたり6×5インチ）を変えたいときなどに便利です

```python:決定境界のとき(class_separator_plot)
classplot.class_separator_plot(clf, x=['petal_width', 'petal_length'],
                               y='species', data=iris,
                               subplot_kws={'figsize': (3, 3)})
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/1222c5f0-3b1b-a6c1-be68-777711e36eae.png)

```python:クラス確率のとき(class_proba_plot)
classplot.class_proba_plot(clf, x=['petal_width', 'petal_length'],
                           y='species', data=iris,
                           subplot_kws={'figsize': (3, 3)})
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/50379241-c459-0cb5-d369-714ff090e640.png)

### contourf_kws
決定境界およびクラス確率図(等高線)描画用メソッドmatplotlib.pyplot.contourfに渡す引数をdict指定できます。
クラス確率図(RGB画像)は、別のメソッドmatplotlib.pyplot.imshowを利用しているので、対応する引数[imshow_kws](https://qiita.com/c60evaporator/items/43866a42e09daebb5cc0#imshow_kws)に引数を渡してください。

※色指定用の引数scatter_colors＆proba_cmap_dictと重複するので、色指定`colors`,`cmap`は渡さないでください
※範囲拡張用の引数chart_extendsigmaと重複するので、範囲指定`extend`は渡さないでください

渡せる引数は、[こちら](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.contourf.html)をご参照ください
（proba_type='contour'のときは、matplotlib.pyplot.contourメソッドなので[こちら](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.contour.html)ご参照ください

```python:alpha(デフォルト0.3)を大きくして色を濃く(class_separator_plot)
classplot.class_separator_plot(clf, x=['petal_width', 'petal_length'],
                               y='species', data=iris,
                               contourf_kws={'alpha': 0.6})
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/165e9014-a2ef-02d7-da6b-20130b0cd8b7.png)

```python:alpha(デフォルト0.5)を大きくして色を濃く(class_proba_plot)
classplot.class_proba_plot(clf, x=['petal_width', 'petal_length'],
                           y='species', data=iris,
                           contourf_kws={'alpha': 0.9})
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/52c1f31f-d31f-b03d-849b-e1484d234bf4.png)
※等高線表示のalphaは各色の濃さを揃えるために補正(補正後alpha = alpha)を掛けているため、alphaを大きくしてもあまり見かけは変化しません。

### scatter_kws
散布図描画用メソッドmatplotlib.pyplot.scatter()に渡す引数をdict指定できます。
渡せる引数は、[こちら](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.scatter.html)をご参照ください

※色指定用の引数scatter_colorsと重複するので、色指定`colors`,`cmap`は渡さないでください
※形状指定用の引数true_marker＆false_markerと重複するので、plot_scatter="error"または"class_error"のとき、形状指定`marker`は渡さないでください。

```python:edgecolorsで散布図の外枠の色を変更(class_separator_plot)
classplot.class_separator_plot(clf, x=['petal_width', 'petal_length'],
                               y='species', data=iris,
                               scatter_kws={'edgecolors': 'red'})
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/08deecfd-68b8-4d27-4411-a58755833e99.png)

```python:edgecolorsで散布図の外枠の色を変更(class_proba_plot)
classplot.class_proba_plot(clf, x=['petal_width', 'petal_length'],
                           y='species', data=iris,
                           scatter_kws={'edgecolors': 'red'})
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/caf06bea-7a66-f3a3-6518-34e558b27984.png)

## class_proba_plotメソッド固有の引数

|引数名|必須引数orオプション|型|デフォルト値|内容|
|---|---|---|---|---|
|plot_border|オプション|bool|True|決定境界線の描画有無|
|proba_class|オプション|str or List[str]|None|確率表示対象のクラス名|
|proba_cmap_dict|オプション|Dict[str, str]|None|クラス確率図のカラーマップ(dict指定)|
|proba_type|オプション|str|'contourf'|クラス確率図の描画種類<br>(等高線'contourf','contour' or RGB画像'imshow')|
|imshow_kws|オプション|Dict|None|RGB画像描画時のmatplotlib.pyplot.imshow()に渡す引数|

### plot_border
決定境界線の描画有無を指定します

```python:決定境界線を描画する
classplot.class_proba_plot(clf, x=['petal_width', 'petal_length'],
                           y='species', data=iris,
                           plot_border=True)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/a7a535de-9939-fe57-8df1-aa6b83874a89.png)

```python:決定境界線を描画しない
classplot.class_proba_plot(clf, x=['petal_width', 'petal_length'],
                           y='species', data=iris,
                           plot_border=False)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/fe03c483-f37b-69b9-9ca7-6a9c1dd3f7a2.png)

### proba_class
確率表示対象のクラス名を指定します。
デフォルトでは全てのクラスを表示対象としているので、一部のクラス確率のみ表示したい場合に使用します。

```python:versicolorのクラス確率のみ表示
classplot.class_proba_plot(clf, x=['petal_width', 'petal_length'],
                           y='species', data=iris,
                           proba_class='versicolor')
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/3ef4202e-ed9e-a2c5-4cff-14f5af38e0bc.png)
リストで複数のクラスを指定する事も可能です

```python:setosa＆virginicaのクラス確率を表示
classplot.class_proba_plot(clf, x=['petal_width', 'petal_length'],
                           y='species', data=iris,
                           proba_class=['setosa', 'virginica'])
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/281f1d0a-e841-f4f8-967e-d6a0dacf4b9b.png)
クラス数が増えると等高線が重なって見辛くなるので、この引数で表示クラスを絞ると、見やすくなるかと思います。

### proba_cmap_dict
クラス確率図のカラーマップを指定します。
クラス名をキー、カラーマップを値としたDict形式で指定する必要があります。

指定できるカラーマップ名は、[こちらをご参照ください](https://beiznotes.org/matplot-cmap-list/)

デフォルトでは、緑('Greens') → 赤('Reds') → 青('Blues') → 茶('YlOrBr')‥の順で指定（散布図のデフォルト色に合わせる）されています。

```python:
classplot.class_proba_plot(clf, x=['petal_width', 'petal_length'],
                           y='species', data=iris,
                           proba_cmap_dict={'setosa':'Greys',
                                            'versicolor':'Oranges',
                                            'virginica':'Purples'})
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/f55766a5-28e4-5efa-a6ee-fb6ef479b2be.png)

### proba_type
クラス確率図の描画種類を指定します。

**'imshow'を指定するとRGB画像**で、
**'contourf'を指定すると塗りつぶしありの等高線**
**'contour'を指定すると塗りつぶしなしの等高線**
で表示されます。

```python:RGB画像
classplot.class_proba_plot(clf, x=['petal_width', 'petal_length'],
                           y='species', data=iris,
                           proba_type='imshow')
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/a1e407f5-179e-a27e-87a6-be2a6e880f8c.png)

```python:等高線(塗りつぶしあり)
classplot.class_proba_plot(clf, x=['petal_width', 'petal_length'],
                           y='species', data=iris,
                           proba_type='contourf')
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/a7a535de-9939-fe57-8df1-aa6b83874a89.png)

```python:等高線(塗りつぶしなし)
classplot.class_proba_plot(clf, x=['petal_width', 'petal_length'],
                           y='species', data=iris,
                           plot_border=False,
                           proba_type='contour')
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/0c526d82-e6ed-a4ef-350b-6cb82fba1f75.png)

### imshow_kws
RGB画像描画用メソッドmatplotlib.pyplot.imshow()に渡す引数をdict指定できます。
proba_type='imshow'のときのみ有効な引数です。

渡せる引数は、[こちら](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.imshow.html)をご参照ください

※色指定はGBR固定なので、、色指定`cmap`は渡さないでください
※範囲拡張用の引数chart_extendsigmaと重複するので、範囲指定`extend`は渡さないでください

```python:alpha(デフォルト0.45)を大きくしてグラフを濃く
classplot.class_proba_plot(clf, x=['petal_width', 'petal_length'],
                           y='species', data=iris,
                           proba_type='imshow',
                           imshow_kws={'alpha':0.8})
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/523f05f6-d502-5895-bb88-fe70dcd20cbb.png)

# 補足：パイプライン処理への対応
標準化等の前処理と組み合わせたモデルを構築したいとき、Scikit-Learnではパイプライン処理がよく使われますが、本ツールの入力にもパイプライン処理が適用できます。

本記事の例ではSVMによる分類の例を使用していますが、SVMは距離に基づくアルゴリズムのため、本来は標準化を前処理に加えた方が良いです。実装例を下記します

```python:サポートベクターマシン＋標準化での適用例
from sklearn.svm import SVC
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler

pipe = Pipeline([("scaler", StandardScaler()), ("svc", SVC())])  # 標準化＋SVMパイプライン
classplot.class_separator_plot(pipe, x=['petal_width', 'petal_length'],
                               y='species', data=iris)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/a615991d-7ee7-95c5-f4a2-eb91da867048.png)

# 余談：その他の便利な分類可視化テクニック
他のライブラリで簡単に実装できるため本ツールでは触れていませんが、分類の可視化には、以下のようなテクニックもよく使われています。
## 特徴量重要度（Feature Importance）
Random ForestやXGBoost、LightGBMなどの決定木系の分類アルゴリズムには、
目的変数の予測に対する、各説明変数の寄与度を定量的にランク付けした、
**「特徴量重要度 (feature importance)」**と呼ばれる指標が存在します。

```python:XGBoostでの実装例
from xgboost import XGBClassifier
import matplotlib.pyplot as plt
# モデルの学習
clf = XGBClassifier()
features = ['petal_width', 'petal_length', 'sepal_width', 'sepal_length']
X = iris[features].values
y = iris['species'].values
clf.fit(X, y)
# 特徴量重要度の取得と可視化
importances = list(clf.feature_importances_)
plt.barh(features, importances)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/b05247bf-73ce-ec51-0c81-639cddf783e8.png)
上図のように、petal_width, petal_lengthの寄与度が高い事が分かります。

この指標は**「どの変数が予測に寄与したか」**という、分類結果を解釈する上で重要な知見を与えてくれます。
本ツールにおいても、[x_chart引数の指定対象特徴量の検討](https://qiita.com/c60evaporator/items/43866a42e09daebb5cc0#x_chart)に役立てられるかと思います。

詳しくは、
[こちら](https://note.com/y_katayama/n/n5882e9b2d15b)や[こちら](https://qiita.com/TomokIshii/items/290adc16e2ca5032ca07)が分かりやすいかと思います。

## 主成分分析（PCA）
結果解釈というより前処理に近い手法となりますが、データの中から変化量の大きい成分（主成分ベクトル）のみを抜き出し、**説明変数の次元数を削減**するアルゴリズムを、「主成分分析」と呼びます。

例えば、4次元のデータを**グラフで可視化しやすい2次元に圧縮**することで、**データの解釈性を向上**させることができます。

上記以外にも、
・[次元の呪い](https://www.techcrowd.jp/machinelearning/dimensions/)防止による汎化性向上
・次元削減による学習処理時間短縮
等のメリットがあります。

一方で、
・主成分以外の近傍関係が消失し、分類性能が落ちる恐れがある（情報欠損をなるべく防ぐよう圧縮してはいます）
・特徴量自体の解釈性が落ちてしまう（元々の特徴量が「大きさ」「色」など解釈性が高く作られているのに対し、PCA後は「大きさ×0.8＋色×0.6」というように混ぜこぜの特徴量となります）
等のデメリットもあります

```python:主成分分析で4→2次元に圧縮後、class_separator_plotで分類可視化
from sklearn.svm import SVC
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
import pandas as pd
from seaborn_analyzer import classplot
features = ['petal_width', 'petal_length', 'sepal_width', 'sepal_length']
X = iris[features].values
y = iris['species'].values
# 前処理として標準化
ss = StandardScaler()
ss.fit(X)  # 標準化パラメータの学習
X_ss = ss.transform(X)  # 学習結果に基づき標準化
# 主成分分析で4→2次元に圧縮
pca = PCA(n_components=2)
pca.fit(X_ss)  # 主成分分析の学習
X_pca = pca.transform(X_ss)  # 学習結果に基づき次元圧縮
iris['pc1'] = pd.Series(X_pca[:, 0])  # 第1主成分をirisに格納
iris['pc2'] = pd.Series(X_pca[:, 1])  # 第2主成分をirisに格納
clf = SVC()
classplot.class_separator_plot(clf, x=['pc1', 'pc2'],
                               y='species', data=iris)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/7666e6bb-79f0-0cc9-6f80-6d6687f3f116.png)

主成分分析は距離に基づいたアルゴリズムのため、前処理として標準化が必要な場合が多いです。
標準化に際して標準偏差で割るかどうかに関しては諸説あるので、[こちら](https://datachemeng.com/autoscaling_before_pca/)や[こちら](https://qiita.com/koshian2/items/2e69cb4981ae8fbd3bda)が参考になるかと思います（今回のirisデータセットは単位系がcmで揃っているので割らなくとも良さそうですが、基本的には上記実装例のようにScikit-LearnのStandardScalerクラスで標準偏差で割ってしまうのが無難みたいです）

## t-SNE
t-SNEとは、点同士の距離の長短を保持しながら次元を圧縮する手法の一つで、
文章解析や画像分類の可視化でよく使われています。

PCA（多次元正規分布が前提）やMDS等他の次元削減手法と比べ、非線形な構造に強い事が利点との事です。
詳細は[こちら](https://qiita.com/stfate/items/8988d01aad9596f9d586)や[こちら](https://qiita.com/sakami/items/bb466161489771f7d2e9)の記事でまとめられております。

```python
import seaborn as sns
from sklearn.preprocessing import StandardScaler
from sklearn.manifold import TSNE
import pandas as pd
features = ['petal_width', 'petal_length', 'sepal_width', 'sepal_length']
X = iris[features].values
y = iris['species'].values
# 前処理として標準化
ss = StandardScaler()
ss.fit(X)  # 標準化パラメータの学習
X_ss = ss.transform(X)  # 学習結果に基づき標準化
# t-SNEで4→2次元に圧縮
tsne = TSNE(n_components=2,  # 圧縮後の次元数
            n_iter=1000,  # 学習繰り返し回数
            random_state=42)  # 乱数シード
points = tsne.fit_transform(X_ss)  # t-SNEの学習と次元圧縮
iris['1st'] = pd.Series(points[:, 0])  # 第1軸をirisに格納
iris['2nd'] = pd.Series(points[:, 1])  # 第2軸をirisに格納
sns.scatterplot(x='1st', y='2nd', hue='species', data=iris)
```
実装には[こちら](https://github.com/olekscode/Examples-PCA-tSNE/blob/master/Python/Visualizing%20Iris%20Dataset%20using%20PCA%20and%20t-SNE.ipynb)を参考にさせて頂きました
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/54ddc7ba-aaf8-3296-5a20-bb965de997cb.png)

試しに標準化しない場合もプロットしましたが、こちらの方がクラスの分離性が良さそうです

```python
import seaborn as sns
from sklearn.preprocessing import StandardScaler
from sklearn.manifold import TSNE
import pandas as pd
features = ['petal_width', 'petal_length', 'sepal_width', 'sepal_length']
# t-SNEで4→2次元に圧縮
tsne = TSNE(n_components=2,  # 圧縮後の次元数
            n_iter=1000,  # 学習繰り返し回数
            random_state=42)  # 乱数シード
points = tsne.fit_transform(iris[features])  # t-SNEの学習と次元圧縮
iris['1st'] = pd.Series(points[:, 0])  # 第1軸をirisに格納
iris['2nd'] = pd.Series(points[:, 1])  # 第2軸をirisに格納
sns.scatterplot(x='1st', y='2nd', hue='species', data=iris)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/886a53ab-db6e-3951-f68e-3abb0a0e9369.png)
[こちらの記事](https://bunsekikobako.com/t-sne-code-example/)では標準化した方が分離性が上がるようですし、irisデータセットでも乱数シードによって標準化した方が分離性が高く見える場合があるので、標準化の要否はデータによりそうです。

なお、t-SNEは可視化に特化した手法であるため、可視化以外の目的での活用は挙動が読めず、あまり薦められないそうです。t-SNEした後にSVMで分類、という使い方は良くないかもしれません。

PCAやt-SNE以外の次元削減のアルゴリズムには、[MDS](https://dev.classmethod.jp/articles/mds-for-parks/)などの圧縮法や、[特徴量選択して不要な変数を捨てる](https://qiita.com/FukuharaYohei/items/db88a8f4c4310afb5a0d)方法等があります。後者は性能は落ちますが（汎化性能は上がることも）、元の特徴量そのものの解釈性を維持できるので、本記事のコンセプトには合致するかと思います。


# 関連記事
本記事は、データの可視化をテーマとしたシリーズものです。よければ下記の記事もどうぞ

・**[散布図と相関係数でデータを一括確認](https://qiita.com/c60evaporator/items/fc531aff0cdbafac0f42)**
![ggpairs_titanic_box_huesurvived.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/aa6b10c7-bb4d-f2be-cce3-81fd50028744.png)

・**[分布の種類を判断](https://qiita.com/c60evaporator/items/20f11b6ee965cec48570)**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/bb3ce2c5-ccec-a009-8e78-2503fdd87a9a.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/39ca660d-0dbd-0570-0ccb-a9b2ad88ade0.png)

・**[回帰結果の可視化](https://qiita.com/c60evaporator/items/c930c822b527f62796ee)**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/610167/02e6083c-a68b-e84a-8b2a-9a799dd1921d.png)