特徴量の可視化と分析
「EDAの基本統計はわかった。だが数字だけでは経営層に伝わらない。」
田中VPoEがスクリーンを見ながら言う。
「離反者と非離反者の違いを、一目でわかるビジュアルにしてくれ。どの特徴量が離反に効いているのか、視覚的に明確にしたい。」
離反者 vs 非離反者の比較分析
カテゴリカル特徴量の比較
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
df = pd.read_csv('WA_Fn-UseC_-Telco-Customer-Churn.csv')
# 主要カテゴリカル特徴量の離反率比較
categorical_features = [
'Contract', 'InternetService', 'PaymentMethod',
'OnlineSecurity', 'TechSupport', 'PaperlessBilling'
]
fig, axes = plt.subplots(2, 3, figsize=(18, 10))
axes = axes.flatten()
for i, col in enumerate(categorical_features):
churn_rates = df.groupby(col)['Churn'].apply(
lambda x: (x == 'Yes').mean() * 100
).sort_values(ascending=False)
churn_rates.plot(kind='bar', ax=axes[i], color='#e74c3c', alpha=0.7)
axes[i].set_title(f'{col}別 離反率(%)')
axes[i].set_ylabel('離反率(%)')
axes[i].axhline(y=26.5, color='gray', linestyle='--', label='全体平均')
axes[i].legend()
axes[i].tick_params(axis='x', rotation=45)
plt.tight_layout()
plt.savefig('categorical_churn_rates.png', dpi=150)
数値特徴量の比較
# 離反者/非離反者の分布を重ねて表示
numeric_features = ['tenure', 'MonthlyCharges', 'TotalCharges']
fig, axes = plt.subplots(1, 3, figsize=(18, 5))
for i, col in enumerate(numeric_features):
for label, color in [('No', '#2ecc71'), ('Yes', '#e74c3c')]:
subset = df[df['Churn'] == label][col].dropna()
axes[i].hist(subset, bins=30, alpha=0.5, label=f'Churn={label}',
color=color, density=True)
axes[i].set_title(f'{col}の分布(離反/非離反)')
axes[i].set_xlabel(col)
axes[i].set_ylabel('密度')
axes[i].legend()
plt.tight_layout()
plt.savefig('numeric_churn_comparison.png', dpi=150)
分布から読み取れるパターン
| 特徴量 | 離反者の傾向 | 非離反者の傾向 |
|---|---|---|
| tenure | 短期(1-12ヶ月)に集中 | 長期に広く分布 |
| MonthlyCharges | 高額帯(70-110)に多い | 低額帯にも分布 |
| TotalCharges | 低額帯に集中 | 高額帯まで広く分布 |
クロス分析(2変数の組み合わせ)
# Contract × tenure × Churn
fig, axes = plt.subplots(1, 3, figsize=(18, 5))
for i, contract in enumerate(['Month-to-month', 'One year', 'Two year']):
subset = df[df['Contract'] == contract]
for label, color in [('No', '#2ecc71'), ('Yes', '#e74c3c')]:
data = subset[subset['Churn'] == label]['tenure']
axes[i].hist(data, bins=20, alpha=0.5, label=f'Churn={label}',
color=color, density=True)
axes[i].set_title(f'{contract}')
axes[i].set_xlabel('tenure(月)')
axes[i].legend()
plt.suptitle('契約形態別 tenure分布(離反/非離反)', fontsize=14)
plt.tight_layout()
plt.savefig('contract_tenure_churn.png', dpi=150)
クロス分析のインサイト
重要な発見:
1. Month-to-month契約 × 短期利用 → 最も離反リスクが高い
2. Fiber optic × オンラインセキュリティなし → 離反率が極めて高い
3. Electronic check × 月額料金高額 → 離反リスク大
4. 高齢者 × 単身 → 離反率が高い傾向
サービス加入パターンの分析
# 各付加サービスの加入有無と離反率
services = ['OnlineSecurity', 'OnlineBackup', 'DeviceProtection',
'TechSupport', 'StreamingTV', 'StreamingMovies']
service_churn = pd.DataFrame()
for svc in services:
rates = df.groupby(svc)['Churn'].apply(
lambda x: (x == 'Yes').mean() * 100
)
service_churn[svc] = rates
print("サービス別離反率(%):")
print(service_churn.T.to_string())
# サービス加入数と離反率の関係
internet_users = df[df['InternetService'] != 'No'].copy()
internet_users['service_count'] = internet_users[services].apply(
lambda row: (row == 'Yes').sum(), axis=1
)
svc_churn = internet_users.groupby('service_count')['Churn'].apply(
lambda x: (x == 'Yes').mean() * 100
)
print(f"\nサービス加入数別離反率:\n{svc_churn}")
サービス加入数と離反率
| サービス加入数 | 離反率 | 解釈 |
|---|---|---|
| 0 | ~50% | 基本サービスのみ → 離反リスク極大 |
| 1-2 | ~35% | 少数サービス → リスク高 |
| 3-4 | ~25% | 中程度のエンゲージメント |
| 5-6 | ~15% | 高エンゲージメント → ロックイン効果 |
可視化のベストプラクティス
効果的な離反分析の可視化:
1. 必ず離反/非離反で色分けする(緑: 非離反、赤: 離反)
2. 全体平均の離反率を基準線として引く
3. 割合(%)と実数の両方を示す
4. クロス分析で交互作用を発見する
5. ビジネスストーリーに沿った順序で並べる
まとめ
| 項目 | ポイント |
|---|---|
| 最大の離反因子 | 契約形態(Month-to-month: 42.7%) |
| 数値の鍵 | tenure(短期ほど離反率高) |
| サービス効果 | 付加サービス加入数が増えるほど離反率低下 |
| 高リスク組み合わせ | Month-to-month × 短期 × Fiber optic |
| 可視化の原則 | 離反/非離反の比較を常に意識する |
チェックリスト
- カテゴリカル特徴量の離反率比較グラフを作成できる
- 数値特徴量の離反/非離反分布を重ねて表示できる
- クロス分析で交互作用を発見できる
- サービス加入数とロックイン効果の関係を説明できる
次のステップへ
特徴量の可視化が完了した。次はデータ前処理に進み、モデル構築に向けたデータの整備を行おう。
推定読了時間: 30分