LESSON

特徴量の可視化と分析

「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分