Saas製品のオンボーディングにおける各設定項目がPaidへの影響度を分析してみた。

# はじめに
自分が今、Saasベンダーに勤めております。
初めて自社製品を利用するユーザー向けに新しいオンボーディングのガイドを設計することが現在の主な仕事です。

したがって、現在のオンボーディングにおける各設定項目の中でどの項目がPaid率への影響が大きいかを分析し、メインの改善内容に示唆だしたいと思います。

# 分析の目的
オンラインユーザー活躍度とPaid率を向上させるため、優先的に取り掛かるべき行動を見出すことです。

# 実行環境
パソコン：MacBook Pro
開発環境：Google Coraboratory
言語：Python
ライブラリ：Pandas、Numpy、Matplotlib

# 分析の流れ
1. csvデータの準備
1. モデルの選択
1. データの前処理
1. ロジステック分析の実行
1. グラフによる可視化

## 1. csvデータの準備
オンボーディングの各項目の設定状況とPaidステータスのデータは自社BIツールからダウンロードしたものです。



### 利用するデータ(一部抜粋)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3897170/4b8fbf8f-6856-466f-9df1-80430bd68bfa.png)


全部で8450本記録です。
※一部モザイクをかけています。

## 2. モデルの選択
**分析したい内容**：各項目の設定状況がPaidの有無にどれぐらい影響あるか
**説明変数**：各項目の設定状況
**目的変数**：Paid状態

目的変数のPaid状態はPaidとUnPaid2つしかないので、**ロジスティック回帰**を使うことにしました。

## 3. データの前処理

#### 必要なライブラリをインポートする
```python
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
```

#### ファイルを読み込む
```python
df = pd.read_csv('/content/onbo.csv')
df
```
<br>
データを出力してみると、*FirstOrderDate* の欄に空値があることがわかりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3897170/835e1f02-16be-4440-8042-99f66c443df4.png)

左から4列目からは各項目になります。中にはUndone,BeforePaid,AfterPaidの値が入っています。

念の為、その他の列に空値があるかを確認します。

#### 空値の確認
```python
df.isnull().sum()
```

出力結果は以下になります。
*FirstOrderDate*以外に、*ProjectCreatTime*列にも空値があります。
ただし、今回の説明変数に*ProjectCreatTime*を使いませんので、特に処理しなくても良いです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3897170/6957db3f-2481-42c9-95ef-82be67a390de.png)


#### データの変換

*FirstOrderDate*の列は
・値がある＝Paidした
・空値＝Paidしていない

また、項目の列は
・Undone＝設定していない
・BeforePaid＝Paidになる前で設定した
・AfterPaid＝Paidになった後で設定した

そこで、*FirstOrderDate*の空値を0に、空値ではないところを1に変換します。
```python
df['First Order Start Date'] = df['First Order Start Date'].apply(lambda x: 0 if pd.isnull(x) else 1)
```

<br>
各項目の列に、以下の様にデータを変換します。

・Undoneを0に
・BeforePaidを1に
・AfterPaidを2に

```python
df = df.replace({'Undone':0,'Before Paid':1,'After Paid':2})
```
<br>

分析する際に、*ProjectID*と*ProjectCreateTime*の2列を使わないので、削除します。
*FirstOrderDate*の名前を*PaidStatus*に変更します。

```python
df = df.drop(['Project ID',' Project Create Time'], axis = 1)
df = df.rename(columns = {'First Order Start Date':'Paid Status'})
df.head()
```
<br>

このようにきれいなデータが完成です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3897170/b70c8a8f-dd47-42d0-913b-8119a7190377.png)


## 4. ロジステック分析の実行

#### データの分割
**モデルの選択**節では、以下を説明しましたので、
その通り説明変数と目的変数を定義し、データを学習用と検証用に分割します。

説明変数：各項目の設定状況
目的変数：Paid状態

```python
x = df.drop('Paid Status', axis = 1)
y = df['Paid Status']

x_train,x_test,y_train,y_test = train_test_split(x,y,test_size = 0.3, random_state= 5)
```

#### 説明変数の標準化

```python
from sklearn.preprocessing import StandardScaler
sc = StandardScaler()
sc.fit(x_train)

x_train_std = sc.transform(x_train)
x_test_std = sc.transform(x_test)
```

#### 分析を実行
```python
model = LogisticRegression()
model.fit(x_train_std, y_train)
print(model.coef_)
print(model.intercept_)
```
<br>

Datacenterの項目を完了したユーザーがPaidに至る確率は高い傾向が見受けられます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3897170/2c4063ad-c75a-4b0a-8287-b2e6f486a46c.png)


#### モデルを評価する
```python
model.score(x_train, y_train)
```
ロジスティック回帰の場合、mode.score()ではAccuracyを返します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3897170/b13dd469-7e19-4f44-92fd-df962be818d0.png)

一見したところAccuracyが高いです。
他の指標も使ってモデルを評価してみます。
```python
from sklearn.metrics import (
    confusion_matrix,
    precision_score,
    recall_score,
    roc_auc_score,
    f1_score)
print(confusion_matrix(y_test,model.predict(x_test_std)))
print(f'precision: {precision_score(y_test,model.predict(x_test_std))}')
print(f'recall: {recall_score(y_test,model.predict(x_test_std))}')
print(f'roc_auc: {roc_auc_score(y_test,model.predict(x_test_std))}')
print(f'f1_score: {f1_score(y_test,model.predict(x_test_std))}')
```
<br>

**出力の結果**
適合率は微妙ですが、ROC-AUCは高い傾向が見受けられます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3897170/5383edd6-7f6b-4c45-a8b3-327e1bf0eb6c.png)

**Precision（適合率）**: 0.916 (92%)
陽性と予測したもののうち、実際に陽性だった割合
**Recall（再現率）**: 0.422 (42%)
実際の陽性のうち、正しく陽性と予測できた割合
**ROC-AUC**: 0.710 (71%)
モデルの全体的な識別能力
**F1スコア**: 0.578 (58%)
PrecisionとRecallの調和平均

<br>

次は**ROC曲線**を出してみます。

```python
from sklearn.metrics import roc_curve, auc

#検証データがクラス1に属する確率
y_score = model.predict_proba(x_test_std)[:, 1] 

#ROC曲線のデータを計算
fpr, tpr, thresholds = roc_curve(y_true=y_test, y_score=y_score)

#ROC曲線のプロット
plt.plot(fpr, tpr, label='roc curve (area = %0.3f)' % auc(fpr, tpr))
plt.plot([0, 1], [0, 1], linestyle='--', label='random')
plt.plot([0, 0, 1], [0, 1, 1], linestyle='--', label='ideal')

#グラフの設定
plt.legend()
plt.xlabel('false positive rate')
plt.ylabel('true positive rate')
plt.show()
```

<br>

**出力結果**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3897170/ee9672d0-67e8-4365-91cf-3b6dca3a6289.png)



| 指標 | 値 | 解釈 |
| ---- | ---- | ---- |
| AUC (曲線下面積) |0.931| 優れた識別能力 |
| 理想曲線との乖離 | 小さい |ほぼ最適に近い性能|
| ランダム予測差 | 43% | (0.930 - 0.5) × 100|



## 5. グラフによる可視化

```python
feature_names = x.columns 
feature_importance = pd.DataFrame({'feature': feature_names, 'importance': model.coef_[0]})
feature_importance = feature_importance.sort_values('importance', ascending=False)


plt.figure(figsize=(10, 6))
sns.barplot(x='importance', y='feature', data=feature_importance)
plt.title('Feature Importance')
plt.show()

print("\n各項目の重要性:")
print(feature_importance)
```
<br>

**出力結果**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3897170/dfca1ba8-b09d-4bbc-beb8-99183611f6d0.png)


# 結論

目的変数のPaidに対して、Datacenterは最も重要度が高く、Heatmapの重要度が低いという結果になりました。
ただし、heatmapは自社製品の主軸機能になりますので、オンボーディング段階では捨てるわけにはいけません。
-0.01という結果から、オンボーディングにおけるheatmap項目の改善を急ぐべきという示唆が得られると思います。