探索的データ分析(EDA)の進め方
「データ辞書ができた。次はEDAだ。データの声に耳を傾けよう。」
田中VPoEがJupyter Notebookを立ち上げる。
「EDAは芸術ではなく科学だ。体系的な手順に沿って、仮説を検証しながら進める。漫然とグラフを作るのではなく、目的を持って分析するんだ。」
EDAの体系的な手順
EDAの5ステップ:
1. データの全体像把握 → shape, info, describe
2. 目的変数の分布確認 → 不均衡度の把握
3. 単変量分析 → 各特徴量の分布
4. 二変量分析 → 特徴量と目的変数の関係
5. 多変量分析 → 特徴量間の相関・交互作用
Step 1: データの全体像
import pandas as pd
import numpy as np
df = pd.read_csv('WA_Fn-UseC_-Telco-Customer-Churn.csv')
# 基本情報
print(f"データサイズ: {df.shape}")
print(f"\nデータ型:\n{df.dtypes}")
print(f"\n基本統計量:\n{df.describe()}")
print(f"\n欠損値:\n{df.isnull().sum()[df.isnull().sum() > 0]}")
# TotalChargesの型変換
df['TotalCharges'] = pd.to_numeric(df['TotalCharges'], errors='coerce')
print(f"\nTotalCharges変換後の欠損: {df['TotalCharges'].isnull().sum()}")
# 11件の欠損が発生 → tenure=0の新規顧客
Step 2: 目的変数の分布
import matplotlib.pyplot as plt
churn_counts = df['Churn'].value_counts()
churn_rate = df['Churn'].value_counts(normalize=True)
print(f"離反分布:\n{churn_counts}")
print(f"\n離反率:\n{churn_rate}")
# 可視化
fig, ax = plt.subplots(figsize=(6, 4))
churn_counts.plot(kind='bar', ax=ax, color=['#2ecc71', '#e74c3c'])
ax.set_title('顧客離反の分布')
ax.set_ylabel('顧客数')
plt.tight_layout()
plt.savefig('churn_distribution.png')
| クラス | 件数 | 割合 |
|---|---|---|
| No(非離反) | 5,174 | 73.5% |
| Yes(離反) | 1,869 | 26.5% |
Step 3: 単変量分析
数値変数の分布
numeric_cols = ['tenure', 'MonthlyCharges', 'TotalCharges']
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
for i, col in enumerate(numeric_cols):
df[col].hist(bins=30, ax=axes[i], edgecolor='black')
axes[i].set_title(f'{col}の分布')
axes[i].set_xlabel(col)
axes[i].set_ylabel('頻度')
plt.tight_layout()
plt.savefig('numeric_distributions.png')
# 統計サマリ
for col in numeric_cols:
print(f"\n{col}:")
print(f" 平均: {df[col].mean():.2f}")
print(f" 中央値: {df[col].median():.2f}")
print(f" 標準偏差: {df[col].std():.2f}")
カテゴリカル変数の分布
categorical_cols = ['gender', 'SeniorCitizen', 'Partner', 'Dependents',
'PhoneService', 'InternetService', 'Contract',
'PaperlessBilling', 'PaymentMethod']
for col in categorical_cols:
print(f"\n{col}:")
print(df[col].value_counts())
Step 4: 二変量分析(特徴量 vs 目的変数)
# カテゴリカル変数と離反率の関係
def plot_churn_rate_by_category(df, col):
churn_rate = df.groupby(col)['Churn'].apply(
lambda x: (x == 'Yes').mean()
).sort_values(ascending=False)
print(f"\n{col}別の離反率:")
print(churn_rate)
return churn_rate
# 主要な仮説の検証
for col in ['Contract', 'InternetService', 'PaymentMethod', 'tenure']:
if col == 'tenure':
# tenureはビニングして分析
df['tenure_group'] = pd.cut(df['tenure'],
bins=[0, 12, 24, 48, 72],
labels=['0-12', '13-24', '25-48', '49-72'])
plot_churn_rate_by_category(df, 'tenure_group')
else:
plot_churn_rate_by_category(df, col)
主要な発見
二変量分析の主要発見:
- Contract: Month-to-month = 42.7%, One year = 11.3%, Two year = 2.8%
- InternetService: Fiber optic = 41.9%, DSL = 19.0%, No = 7.4%
- PaymentMethod: Electronic check = 45.3%, 他は15-18%
- tenure: 0-12ヶ月 = 47.7%, 49-72ヶ月 = 6.6%
Step 5: 多変量分析
# 数値変数の相関行列
numeric_df = df[['tenure', 'MonthlyCharges', 'TotalCharges']].copy()
numeric_df['Churn_numeric'] = (df['Churn'] == 'Yes').astype(int)
correlation = numeric_df.corr()
print("相関行列:")
print(correlation)
# ヒートマップ
import seaborn as sns
plt.figure(figsize=(8, 6))
sns.heatmap(correlation, annot=True, cmap='coolwarm', center=0)
plt.title('相関行列')
plt.tight_layout()
plt.savefig('correlation_heatmap.png')
EDAから得られるインサイト
| 仮説 | 検証結果 | 判定 |
|---|---|---|
| H1: Month-to-month契約は離反率が高い | 42.7% vs 2.8%(Two year) | 支持 |
| H4: Fiber optic顧客は離反率が高い | 41.9% vs 19.0%(DSL) | 支持 |
| H7: 利用期間が短い顧客は離反しやすい | 0-12ヶ月: 47.7% | 支持 |
| H10: Electronic check支払いは離反率が高い | 45.3% vs 15-18% | 支持 |
まとめ
| 項目 | ポイント |
|---|---|
| EDAの手順 | 全体像→目的変数→単変量→二変量→多変量 |
| 不均衡度 | 離反26.5%(極端ではないが考慮必要) |
| 最強のシグナル | 契約形態(Month-to-month)が最も離反率に影響 |
| 数値変数 | tenureが離反と強い負の相関 |
| 仮説検証 | 主要仮説はすべて支持された |
チェックリスト
- EDAの5ステップを実行できる
- 単変量・二変量分析の手法を使える
- 離反率が高い特徴量の組み合わせを特定できた
- 仮説の検証結果をまとめられた
次のステップへ
EDAで全体像を把握できた。次は特徴量の可視化をさらに深め、離反者と非離反者の違いをより詳細に分析していこう。
推定読了時間: 30分