データ分析手法完全ガイド!基礎から実践まで使える分析テクニック総まとめ

「データ分析にはどんな手法があるの?」「目的に応じてどの分析手法を選べばいいかわからない」「基本的な統計分析から機械学習まで体系的に学びたい」
データ分析を始めたばかりの方から、実務でより高度な分析を行いたい方まで、このような疑問を抱くのは自然なことです。データ分析には記述統計から機械学習まで数多くの手法があり、それぞれ適用場面や得られる洞察が異なります。
しかし、各手法の特徴と適用場面を体系的に理解すれば、ビジネス課題に最適な分析アプローチを選択できるようになります。実際に、適切な分析手法を使い分けることで、データから価値ある洞察を効率的に導出している分析者が数多くいます。
本記事では、データ分析の基本的な手法分類から、具体的な分析テクニック、実務での活用方法まで、包括的に解説します。この記事を読めば、あなたも目的に応じた最適な分析手法を選択できるようになるでしょう。
データ分析手法の体系的分類
分析の目的による分類
データ分析は、その目的により大きく4つのカテゴリに分類されます。この分類を理解することで、ビジネス課題に対して適切なアプローチを選択できるようになります。
1. 記述的分析(Descriptive Analytics)
目的: 「何が起こったか?」を理解する
記述的分析は、過去に何が起こったかを客観的に把握するための分析です。データの現状を数値やグラフで可視化し、基本的な傾向やパターンを明らかにします。ビジネス報告書やダッシュボードで最も多く使用される分析タイプです。
主な手法と実装例:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from datetime import datetime, timedelta
# 売上データの記述的分析例
def descriptive_analysis_example():
# サンプルデータの作成
np.random.seed(42)
dates = pd.date_range(start='2023-01-01', end='2023-12-31', freq='D')
# 季節性のある売上データを生成
seasonal_effect = np.sin(2 * np.pi * np.arange(len(dates)) / 365) * 100000
trend = np.linspace(0, 50000, len(dates))
random_noise = np.random.normal(0, 20000, len(dates))
sales_data = pd.DataFrame({
'date': dates,
'sales': 300000 + seasonal_effect + trend + random_noise,
'region': np.random.choice(['東京', '大阪', '名古屋', '福岡'], len(dates)),
'product_category': np.random.choice(['電子機器', '衣類', '食品', '書籍'], len(dates))
})
# データの基本統計量
print("=== 基本統計量 ===")
print(sales_data['sales'].describe())
# 月別売上の集計
sales_data['month'] = sales_data['date'].dt.month
monthly_sales = sales_data.groupby('month')['sales'].agg(['sum', 'mean', 'count'])
print("\n=== 月別売上サマリー ===")
print(monthly_sales)
# 可視化
fig, axes = plt.subplots(2, 2, figsize=(15, 10))
# 1. 売上の時系列推移
daily_sales = sales_data.groupby('date')['sales'].sum()
axes[0,0].plot(daily_sales.index, daily_sales.values)
axes[0,0].set_title('日別売上推移')
axes[0,0].set_xlabel('日付')
axes[0,0].set_ylabel('売上(円)')
# 2. 月別売上の箱ひげ図
sales_data.boxplot(column='sales', by='month', ax=axes[0,1])
axes[0,1].set_title('月別売上分布')
axes[0,1].set_xlabel('月')
axes[0,1].set_ylabel('売上(円)')
# 3. 地域別売上の棒グラフ
region_sales = sales_data.groupby('region')['sales'].sum()
axes[1,0].bar(region_sales.index, region_sales.values)
axes[1,0].set_title('地域別売上')
axes[1,0].set_xlabel('地域')
axes[1,0].set_ylabel('合計売上(円)')
# 4. 商品カテゴリ別売上の円グラフ
category_sales = sales_data.groupby('product_category')['sales'].sum()
axes[1,1].pie(category_sales.values, labels=category_sales.index, autopct='%1.1f%%')
axes[1,1].set_title('商品カテゴリ別売上構成比')
plt.tight_layout()
plt.show()
# 主要なKPIの計算
total_sales = sales_data['sales'].sum()
average_daily_sales = sales_data.groupby('date')['sales'].sum().mean()
growth_rate = ((sales_data[sales_data['date'] >= '2023-10-01']['sales'].mean() -
sales_data[sales_data['date'] < '2023-04-01']['sales'].mean()) /
sales_data[sales_data['date'] < '2023-04-01']['sales'].mean()) * 100
print(f"\n=== 主要KPI ===")
print(f"年間総売上: {total_sales:,.0f}円")
print(f"平均日別売上: {average_daily_sales:,.0f}円")
print(f"売上成長率(前半vs後半): {growth_rate:.1f}%")
return sales_data
# 実行
sales_data = descriptive_analysis_example()
記述的分析の活用場面:
- 月次・四半期業績報告
- ダッシュボードの基本指標
- 顧客行動の現状把握
- 在庫・売上の推移確認
2. 診断的分析(Diagnostic Analytics)
目的: 「なぜ起こったか?」を理解する
診断的分析は、記述的分析で発見した現象の原因を探る分析です。相関関係の調査、要因分析、異常値の特定などを通じて、「なぜそうなったのか」を明らかにします。
実装例:
# 診断的分析の実装例
def diagnostic_analysis_example(sales_data):
"""売上変動の要因分析"""
# 1. 相関分析
# 数値データの相関を調査
sales_data['day_of_week'] = sales_data['date'].dt.dayofweek
sales_data['is_weekend'] = sales_data['day_of_week'].isin([5, 6]).astype(int)
sales_data['quarter'] = sales_data['date'].dt.quarter
# 相関行列の計算
numeric_columns = ['sales', 'month', 'day_of_week', 'is_weekend', 'quarter']
correlation_matrix = sales_data[numeric_columns].corr()
plt.figure(figsize=(10, 8))
sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm', center=0)
plt.title('売上関連要因の相関分析')
plt.show()
# 2. 要因別分析
print("=== 要因別売上分析 ===")
# 曜日別の売上パターン
weekday_sales = sales_data.groupby('day_of_week')['sales'].mean()
weekday_names = ['月', '火', '水', '木', '金', '土', '日']
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.bar(weekday_names, weekday_sales)
plt.title('曜日別平均売上')
plt.xlabel('曜日')
plt.ylabel('平均売上(円)')
# 地域×商品カテゴリのクロス分析
cross_analysis = sales_data.pivot_table(
values='sales',
index='region',
columns='product_category',
aggfunc='mean'
)
plt.subplot(1, 2, 2)
sns.heatmap(cross_analysis, annot=True, fmt='.0f', cmap='viridis')
plt.title('地域×商品カテゴリ 平均売上')
plt.show()
# 3. 異常値検出と分析
from scipy import stats
# Z-scoreによる異常値検出
z_scores = np.abs(stats.zscore(sales_data['sales']))
outliers = sales_data[z_scores > 3]
print(f"\n異常値の数: {len(outliers)}件")
if len(outliers) > 0:
print("異常値の特徴:")
print(outliers[['date', 'sales', 'region', 'product_category']].head())
# 4. セグメント別分析
# 売上をパフォーマンス別にセグメント化
sales_quartiles = sales_data['sales'].quantile([0.25, 0.5, 0.75])
def categorize_performance(sales):
if sales <= sales_quartiles[0.25]:
return '低売上'
elif sales <= sales_quartiles[0.5]:
return '中売上(低)'
elif sales <= sales_quartiles[0.75]:
return '中売上(高)'
else:
return '高売上'
sales_data['performance_segment'] = sales_data['sales'].apply(categorize_performance)
# セグメント別の特徴分析
segment_analysis = sales_data.groupby('performance_segment').agg({
'sales': ['count', 'mean'],
'region': lambda x: x.mode().iloc[0],
'product_category': lambda x: x.mode().iloc[0]
})
print("\n=== セグメント別特徴 ===")
print(segment_analysis)
return outliers, cross_analysis
# 実行
outliers, cross_analysis = diagnostic_analysis_example(sales_data)
3. 予測的分析(Predictive Analytics)
目的: 「何が起こりそうか?」を予測する
予測的分析は、過去のデータパターンを基に将来の出来事を予測する分析です。機械学習アルゴリズムや統計的手法を用いて、将来の売上、顧客行動、リスクなどを予測します。
実装例:
# 予測的分析の実装例
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.metrics import mean_absolute_error, mean_squared_error
from sklearn.preprocessing import LabelEncoder
import warnings
warnings.filterwarnings('ignore')
def predictive_analysis_example(sales_data):
"""売上予測モデルの構築"""
# 1. 特徴量エンジニアリング
# 予測に使用する特徴量を作成
prediction_data = sales_data.copy()
# 日付関連特徴量
prediction_data['year'] = prediction_data['date'].dt.year
prediction_data['month'] = prediction_data['date'].dt.month
prediction_data['day'] = prediction_data['date'].dt.day
prediction_data['day_of_year'] = prediction_data['date'].dt.dayofyear
prediction_data['week_of_year'] = prediction_data['date'].dt.isocalendar().week
# 移動平均(過去の傾向を反映)
prediction_data = prediction_data.sort_values('date')
prediction_data['sales_ma_7'] = prediction_data['sales'].rolling(window=7).mean()
prediction_data['sales_ma_30'] = prediction_data['sales'].rolling(window=30).mean()
# ラグ特徴量(過去の売上)
prediction_data['sales_lag_1'] = prediction_data['sales'].shift(1)
prediction_data['sales_lag_7'] = prediction_data['sales'].shift(7)
# カテゴリ変数のエンコーディング
le_region = LabelEncoder()
le_category = LabelEncoder()
prediction_data['region_encoded'] = le_region.fit_transform(prediction_data['region'])
prediction_data['category_encoded'] = le_category.fit_transform(prediction_data['product_category'])
# 欠損値を削除(移動平均・ラグのため)
prediction_data = prediction_data.dropna()
# 2. 予測モデルの構築
# 特徴量とターゲットの定義
feature_columns = [
'month', 'day', 'day_of_year', 'week_of_year', 'day_of_week',
'region_encoded', 'category_encoded', 'is_weekend',
'sales_ma_7', 'sales_ma_30', 'sales_lag_1', 'sales_lag_7'
]
X = prediction_data[feature_columns]
y = prediction_data['sales']
# 時系列データなので、時間順で分割
split_date = prediction_data['date'].quantile(0.8)
train_mask = prediction_data['date'] <= split_date
X_train, X_test = X[train_mask], X[~train_mask]
y_train, y_test = y[train_mask], y[~train_mask]
# モデルの学習
rf_model = RandomForestRegressor(n_estimators=100, random_state=42)
rf_model.fit(X_train, y_train)
# 予測と評価
y_pred = rf_model.predict(X_test)
mae = mean_absolute_error(y_test, y_pred)
rmse = np.sqrt(mean_squared_error(y_test, y_pred))
mape = np.mean(np.abs((y_test - y_pred) / y_test)) * 100
print("=== 予測モデルの性能 ===")
print(f"MAE: {mae:,.0f}")
print(f"RMSE: {rmse:,.0f}")
print(f"MAPE: {mape:.1f}%")
# 3. 特徴量重要度の分析
feature_importance = pd.DataFrame({
'feature': feature_columns,
'importance': rf_model.feature_importances_
}).sort_values('importance', ascending=False)
plt.figure(figsize=(10, 6))
sns.barplot(data=feature_importance, x='importance', y='feature')
plt.title('売上予測における特徴量重要度')
plt.xlabel('重要度')
plt.show()
# 4. 予測結果の可視化
test_dates = prediction_data[~train_mask]['date']
plt.figure(figsize=(12, 6))
plt.plot(test_dates, y_test.values, label='実際の売上', alpha=0.7)
plt.plot(test_dates, y_pred, label='予測売上', alpha=0.7)
plt.title('売上予測結果')
plt.xlabel('日付')
plt.ylabel('売上(円)')
plt.legend()
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
# 5. 将来予測の例
# 最新データの特徴量で翌日を予測
latest_data = prediction_data.iloc[-1:][feature_columns]
future_prediction = rf_model.predict(latest_data)
print(f"\n=== 将来予測例 ===")
print(f"翌日の予測売上: {future_prediction[0]:,.0f}円")
return rf_model, feature_importance, mae
# 実行
rf_model, importance_df, mae = predictive_analysis_example(sales_data)
4. 処方的分析(Prescriptive Analytics)
目的: 「何をすべきか?」の推奨を提供する
処方的分析は、予測結果を基に最適な行動やアクションを推奨する分析です。最適化アルゴリズムやシミュレーションを使用して、目標達成のための具体的な施策を提案します。
実装例:
# 処方的分析の実装例
from scipy.optimize import minimize
import itertools
def prescriptive_analysis_example(sales_data, rf_model):
"""最適な販売戦略の提案"""
# 1. シナリオ分析
# 異なる条件下での売上予測
def simulate_sales_scenarios():
scenarios = {
'現状維持': {'region_weight': [0.25, 0.25, 0.25, 0.25], 'category_weight': [0.25, 0.25, 0.25, 0.25]},
'東京集中': {'region_weight': [0.5, 0.2, 0.2, 0.1], 'category_weight': [0.25, 0.25, 0.25, 0.25]},
'電子機器特化': {'region_weight': [0.25, 0.25, 0.25, 0.25], 'category_weight': [0.5, 0.2, 0.2, 0.1]}
}
results = {}
for scenario_name, weights in scenarios.items():
# シナリオに基づく予測売上計算(簡略化)
base_sales = sales_data['sales'].mean()
# 重み付き効果の計算
region_effect = np.sum(np.array(weights['region_weight']) * np.array([1.1, 1.0, 0.95, 0.9]))
category_effect = np.sum(np.array(weights['category_weight']) * np.array([1.15, 1.0, 0.9, 0.95]))
predicted_sales = base_sales * region_effect * category_effect
results[scenario_name] = predicted_sales
return results
scenario_results = simulate_sales_scenarios()
print("=== シナリオ分析結果 ===")
for scenario, sales in scenario_results.items():
print(f"{scenario}: {sales:,.0f}円(変化率: {((sales/scenario_results['現状維持'])-1)*100:.1f}%)")
# 2. 最適化問題の解決
# 予算配分の最適化
def optimize_budget_allocation():
"""マーケティング予算の最適配分"""
# 制約条件と目的関数の定義
total_budget = 1000000 # 100万円の予算
# 各チャネルのROI(仮の値)
channel_roi = {
'デジタル広告': 2.5,
'テレビCM': 1.8,
'イベント': 3.0,
'SNS': 2.2
}
# 最適化関数
def objective(allocation):
return -sum(allocation[i] * list(channel_roi.values())[i] for i in range(len(allocation)))
# 制約条件
constraints = [
{'type': 'eq', 'fun': lambda x: sum(x) - total_budget}, # 予算制約
{'type': 'ineq', 'fun': lambda x: x[0]}, # 非負制約
{'type': 'ineq', 'fun': lambda x: x[1]},
{'type': 'ineq', 'fun': lambda x: x[2]},
{'type': 'ineq', 'fun': lambda x: x[3]},
]
# 初期値
x0 = [total_budget/4] * 4
# 最適化実行
result = minimize(objective, x0, method='SLSQP', constraints=constraints)
optimal_allocation = dict(zip(channel_roi.keys(), result.x))
optimal_roi = -result.fun
return optimal_allocation, optimal_roi
optimal_allocation, optimal_roi = optimize_budget_allocation()
print(f"\n=== 最適予算配分 ===")
for channel, amount in optimal_allocation.items():
print(f"{channel}: {amount:,.0f}円 ({amount/sum(optimal_allocation.values())*100:.1f}%)")
print(f"期待総リターン: {optimal_roi:,.0f}円")
# 3. A/Bテスト設計の提案
def recommend_ab_test():
"""A/Bテストの推奨設計"""
current_conversion = 0.05 # 現在のコンバージョン率5%
target_improvement = 0.20 # 20%の改善を目標
significance_level = 0.05
power = 0.8
# 必要サンプルサイズの計算(簡略化)
effect_size = target_improvement * current_conversion
baseline_rate = current_conversion
# 二項検定のサンプルサイズ計算(近似)
import math
z_alpha = 1.96 # 95%信頼区間
z_beta = 0.84 # 80%検出力
n = (2 * (z_alpha + z_beta)**2 * baseline_rate * (1 - baseline_rate)) / (effect_size**2)
n = math.ceil(n)
recommendations = {
'推奨テスト期間': '2-4週間',
'必要サンプルサイズ': f'{n:,}人(各グループ)',
'推奨テスト項目': [
'CTAボタンの色・位置',
'ページのレイアウト',
'価格表示方法',
'コンテンツの訴求ポイント'
],
'成功指標': [
'コンバージョン率',
'クリック率',
'平均注文金額',
'離脱率'
]
}
return recommendations
ab_test_recommendations = recommend_ab_test()
print(f"\n=== A/Bテスト推奨設計 ===")
for key, value in ab_test_recommendations.items():
if isinstance(value, list):
print(f"{key}:")
for item in value:
print(f" - {item}")
else:
print(f"{key}: {value}")
# 4. 行動推奨の生成
action_recommendations = []
# 売上予測精度に基づく推奨
if mae < 50000: # 予測精度が高い場合
action_recommendations.append("予測精度が高いため、在庫計画に予測値を活用することを推奨")
else:
action_recommendations.append("予測精度を向上させるため、追加の特徴量収集を推奨")
# シナリオ分析結果に基づく推奨
best_scenario = max(scenario_results, key=scenario_results.get)
if best_scenario != '現状維持':
action_recommendations.append(f"売上向上のため「{best_scenario}」戦略の実施を推奨")
print(f"\n=== 推奨アクション ===")
for i, recommendation in enumerate(action_recommendations, 1):
print(f"{i}. {recommendation}")
return scenario_results, optimal_allocation, ab_test_recommendations
# 実行
scenario_results, allocation, ab_recommendations = prescriptive_analysis_example(sales_data, rf_model)
統計分析の基本手法
記述統計
記述統計は、データの基本的な性質を数値で要約する手法です。平均、分散、分布などを通じてデータの全体像を把握します。
中心傾向の測定
実装例:
def comprehensive_descriptive_statistics():
"""包括的な記述統計分析"""
# サンプルデータの作成(顧客の年収データ)
np.random.seed(42)
# 複数のグループを含むデータ
data = {
'20代': np.random.normal(400, 100, 1000), # 平均400万、標準偏差100万
'30代': np.random.normal(550, 120, 1000), # 平均550万、標準偏差120万
'40代': np.random.normal(700, 150, 1000), # 平均700万、標準偏差150万
'50代': np.random.normal(650, 140, 1000), # 平均650万、標準偏差140万
}
# データフレームに変換
income_data = []
for age_group, incomes in data.items():
for income in incomes:
income_data.append({'age_group': age_group, 'income': max(200, income)}) # 最小200万
df = pd.DataFrame(income_data)
# 1. 基本統計量の計算
print("=== 年代別年収の基本統計量 ===")
stats_summary = df.groupby('age_group')['income'].agg([
'count', # データ数
'mean', # 平均値
'median', # 中央値
'std', # 標準偏差
'min', # 最小値
'max', # 最大値
lambda x: x.quantile(0.25), # 第1四分位数
lambda x: x.quantile(0.75), # 第3四分位数
lambda x: x.mode().iloc[0] if len(x.mode()) > 0 else np.nan # 最頻値
])
stats_summary.columns = ['データ数', '平均値', '中央値', '標準偏差', '最小値', '最大値', 'Q1', 'Q3', '最頻値']
print(stats_summary.round(1))
# 2. 分布の形状分析
from scipy import stats
print(f"\n=== 分布の形状分析 ===")
for age_group in data.keys():
group_data = df[df['age_group'] == age_group]['income']
# 歪度(Skewness)と尖度(Kurtosis)
skewness = stats.skew(group_data)
kurtosis = stats.kurtosis(group_data)
print(f"{age_group}:")
print(f" 歪度: {skewness:.3f} ({'右に歪んでいる' if skewness > 0 else '左に歪んでいる' if skewness < 0 else '対称的'})")
print(f" 尖度: {kurtosis:.3f} ({'尖っている' if kurtosis > 0 else '平たい' if kurtosis < 0 else '正規分布的'})")
# 3. 可視化
fig, axes = plt.subplots(2, 2, figsize=(15, 10))
# ヒストグラム
axes[0,0].hist([df[df['age_group'] == age]['income'] for age in data.keys()],
bins=30, alpha=0.7, label=list(data.keys()))
axes[0,0].set_title('年代別年収分布')
axes[0,0].set_xlabel('年収(万円)')
axes[0,0].set_ylabel('頻度')
axes[0,0].legend()
# 箱ひげ図
df.boxplot(column='income', by='age_group', ax=axes[0,1])
axes[0,1].set_title('年代別年収の箱ひげ図')
axes[0,1].set_xlabel('年代')
axes[0,1].set_ylabel('年収(万円)')
# 累積分布
for age_group in data.keys():
group_data = df[df['age_group'] == age_group]['income'].sort_values()
cumulative = np.arange(1, len(group_data) + 1) / len(group_data)
axes[1,0].plot(group_data, cumulative, label=age_group)
axes[1,0].set_title('年代別年収の累積分布')
axes[1,0].set_xlabel('年収(万円)')
axes[1,0].set_ylabel('累積確率')
axes[1,0].legend()
# バイオリンプロット
age_groups = [df[df['age_group'] == age]['income'] for age in data.keys()]
parts = axes[1,1].violinplot(age_groups, positions=range(len(data.keys())), showmeans=True)
axes[1,1].set_title('年代別年収分布(詳細)')
axes[1,1].set_xlabel('年代')
axes[1,1].set_ylabel('年収(万円)')
axes[1,1].set_xticks(range(len(data.keys())))
axes[1,1].set_xticklabels(list(data.keys()))
plt.tight_layout()
plt.show()
# 4. パーセンタイル分析
print(f"\n=== パーセンタイル分析 ===")
percentiles = [10, 25, 50, 75, 90, 95, 99]
percentile_analysis = df.groupby('age_group')['income'].quantile([p/100 for p in percentiles]).unstack()
percentile_analysis.columns = [f'{p}%ile' for p in percentiles]
print(percentile_analysis.round(1))
return df, stats_summary
# 実行
income_df, income_stats = comprehensive_descriptive_statistics()
推測統計
推測統計は、サンプルデータから母集団の特性を推定したり、仮説の妥当性を検証したりする手法です。
仮説検定
実装例:
def comprehensive_hypothesis_testing():
"""包括的な仮説検定の実装"""
# サンプルデータの作成
np.random.seed(42)
# A/Bテストデータ(ウェブサイトのコンバージョン率)
group_a = np.random.binomial(1, 0.05, 10000) # コントロール群:5%のコンバージョン
group_b = np.random.binomial(1, 0.057, 10000) # テスト群:5.7%のコンバージョン
# 顧客満足度データ(5点尺度)
satisfaction_before = np.random.normal(3.2, 0.8, 500)
satisfaction_after = np.random.normal(3.6, 0.9, 500)
# 売上データ(3つのグループ)
sales_region_a = np.random.normal(100000, 20000, 200)
sales_region_b = np.random.normal(110000, 25000, 200)
sales_region_c = np.random.normal(105000, 22000, 200)
print("=== 仮説検定の実行 ===\n")
# 1. 二項検定(コンバージョン率の比較)
from scipy import stats
from statsmodels.stats.proportion import proportions_ztest
conv_a = np.sum(group_a)
conv_b = np.sum(group_b)
n_a = len(group_a)
n_b = len(group_b)
# Z検定による比較
count = np.array([conv_b, conv_a])
nobs = np.array([n_b, n_a])
z_stat, p_value = proportions_ztest(count, nobs)
print("1. A/Bテスト(コンバージョン率比較)")
print(f" グループA: {conv_a}/{n_a} = {conv_a/n_a:.3%}")
print(f" グループB: {conv_b}/{n_b} = {conv_b/n_b:.3%}")
print(f" Z統計量: {z_stat:.3f}")
print(f" p値: {p_value:.6f}")
print(f" 結果: {'有意差あり' if p_value < 0.05 else '有意差なし'}(α=0.05)")
# 効果量の計算(Cohen's h)
p1, p2 = conv_a/n_a, conv_b/n_b
cohens_h = 2 * (np.arcsin(np.sqrt(p1)) - np.arcsin(np.sqrt(p2)))
print(f" 効果量(Cohen's h): {abs(cohens_h):.3f}")
# 2. 対応のあるt検定(満足度の変化)
print(f"\n2. 顧客満足度の変化(対応のあるt検定)")
# 同じ顧客の前後比較をシミュレート
paired_diff = satisfaction_after - satisfaction_before
t_stat, p_value = stats.ttest_rel(satisfaction_after, satisfaction_before)
print(f" 改善前平均: {np.mean(satisfaction_before):.2f}")
print(f" 改善後平均: {np.mean(satisfaction_after):.2f}")
print(f" 平均差: {np.mean(paired_diff):.2f}")
print(f" t統計量: {t_stat:.3f}")
print(f" p値: {p_value:.6f}")
print(f" 結果: {'有意な改善あり' if p_value < 0.05 else '有意な改善なし'}(α=0.05)")
# 効果量の計算(Cohen's d)
cohens_d = np.mean(paired_diff) / np.std(paired_diff)
print(f" 効果量(Cohen's d): {cohens_d:.3f}")
# 3. 分散分析(ANOVA)- 3つの地域の売上比較
print(f"\n3. 地域別売上比較(分散分析)")
f_stat, p_value = stats.f_oneway(sales_region_a, sales_region_b, sales_region_c)
print(f" 地域A平均: {np.mean(sales_region_a):,.0f}円")
print(f" 地域B平均: {np.mean(sales_region_b):,.0f}円")
print(f" 地域C平均: {np.mean(sales_region_c):,.0f}円")
print(f" F統計量: {f_stat:.3f}")
print(f" p値: {p_value:.6f}")
print(f" 結果: {'地域間に有意差あり' if p_value < 0.05 else '地域間に有意差なし'}(α=0.05)")
# 事後検定(Tukey's HSD)
if p_value < 0.05:
from scipy.stats import tukey_hsd
res = tukey_hsd(sales_region_a, sales_region_b, sales_region_c)
print(f" 事後検定結果:")
comparisons = [('A vs B', 0, 1), ('A vs C', 0, 2), ('B vs C', 1, 2)]
for comp_name, i, j in comparisons:
pvalue_tukey = res.pvalue[i, j]
print(f" {comp_name}: p値={pvalue_tukey:.4f} ({'有意差あり' if pvalue_tukey < 0.05 else '有意差なし'})")
# 4. カイ二乗検定(カテゴリカルデータ)
print(f"\n4. カテゴリカルデータの独立性検定")
# 購買チャネル × 年代のクロス集計表を作成
channels = ['店舗', 'オンライン', 'アプリ']
age_groups = ['20代', '30代', '40代', '50代']
# サンプルデータ作成
np.random.seed(42)
cross_table = np.random.multinomial(1000, [0.12, 0.08, 0.15, 0.10, 0.18, 0.12, 0.10, 0.08, 0.07]).reshape(3, 3)
cross_table = pd.DataFrame(cross_table, index=channels, columns=age_groups[:3])
print(" 購買チャネル × 年代 クロス集計表:")
print(cross_table)
chi2, p_value, dof, expected = stats.chi2_contingency(cross_table)
print(f"\n カイ二乗統計量: {chi2:.3f}")
print(f" 自由度: {dof}")
print(f" p値: {p_value:.6f}")
print(f" 結果: {'購買チャネルと年代に関連あり' if p_value < 0.05 else '購買チャネルと年代に関連なし'}(α=0.05)")
# 5. 検定力分析
print(f"\n5. 検定力分析")
# サンプルサイズと検出可能な効果量の関係
from statsmodels.stats.power import ttest_power, tt_solve_power
# 現在のサンプルサイズでの検定力
effect_size = 0.3 # 中程度の効果量
sample_size = 100
power = ttest_power(effect_size, sample_size, alpha=0.05)
print(f" 効果量0.3、サンプルサイズ{sample_size}での検定力: {power:.3f}")
# 検定力0.8を達成するのに必要なサンプルサイズ
required_n = tt_solve_power(effect_size=0.3, power=0.8, alpha=0.05)
print(f" 検定力0.8を得るのに必要なサンプルサイズ: {required_n:.0f}")
return {
'ab_test': {'z_stat': z_stat, 'p_value': p_value, 'effect_size': cohens_h},
'satisfaction': {'t_stat': t_stat, 'p_value': p_value, 'effect_size': cohens_d},
'anova': {'f_stat': f_stat, 'p_value': p_value},
'chi2': {'chi2': chi2, 'p_value': p_value}
}
# 実行
hypothesis_results = comprehensive_hypothesis_testing()
相関・回帰分析
相関・回帰分析は、変数間の関係性を定量的に把握する手法です。
実装例:
def comprehensive_correlation_regression():
"""相関・回帰分析の包括的実装"""
# より複雑な関係性を持つデータセット作成
np.random.seed(42)
n_samples = 1000
# 広告費、気温、競合数、季節性が売上に影響するモデル
advertising_cost = np.random.uniform(50, 500, n_samples) # 広告費(万円)
temperature = np.random.normal(20, 10, n_samples) # 気温(℃)
competitors = np.random.poisson(3, n_samples) # 競合店数
season = np.random.choice([0, 1, 2, 3], n_samples) # 季節(0:春、1:夏、2:秋、3:冬)
# 複雑な非線形関係を含む売上モデル
sales = (
100 + # ベース売上
advertising_cost * 0.8 + # 広告効果
np.where(temperature > 25, (temperature - 25) * 2, 0) + # 暑い日の効果
np.where(temperature < 10, (10 - temperature) * 1.5, 0) + # 寒い日の効果
competitors * (-5) + # 競合の負の影響
np.array([10, 20, 15, 25])[season] + # 季節効果
advertising_cost * temperature * 0.001 + # 交互作用
np.random.normal(0, 20, n_samples) # ノイズ
)
df = pd.DataFrame({
'sales': sales,
'advertising_cost': advertising_cost,
'temperature': temperature,
'competitors': competitors,
'season': season
})
# 1. 相関分析
print("=== 相関分析 ===")
# ピアソン相関
numeric_cols = ['sales', 'advertising_cost', 'temperature', 'competitors']
correlation_matrix = df[numeric_cols].corr()
print("ピアソン相関係数:")
print(correlation_matrix.round(3))
# スピアマン相関(順位相関)
spearman_corr = df[numeric_cols].corr(method='spearman')
print(f"\nスピアマン相関係数:")
print(spearman_corr.round(3))
# 相関の有意性検定
from scipy.stats import pearsonr, spearmanr
print(f"\n相関の有意性検定:")
for col in ['advertising_cost', 'temperature', 'competitors']:
r, p = pearsonr(df['sales'], df[col])
print(f"売上 vs {col}: r={r:.3f}, p={p:.4f} ({'有意' if p < 0.05 else '非有意'})")
# 2. 可視化
fig, axes = plt.subplots(2, 3, figsize=(18, 10))
# 相関行列のヒートマップ
sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm', center=0, ax=axes[0,0])
axes[0,0].set_title('ピアソン相関行列')
# 散布図と回帰直線
sns.scatterplot(data=df, x='advertising_cost', y='sales', ax=axes[0,1])
sns.regplot(data=df, x='advertising_cost', y='sales', ax=axes[0,1], scatter=False, color='red')
axes[0,1].set_title('広告費 vs 売上')
sns.scatterplot(data=df, x='temperature', y='sales', ax=axes[0,2])
sns.regplot(data=df, x='temperature', y='sales', ax=axes[0,2], scatter=False, color='red')
axes[0,2].set_title('気温 vs 売上')
# 残差プロット(非線形性の確認)
from sklearn.linear_model import LinearRegression
X_simple = df[['advertising_cost']]
y = df['sales']
lr_simple = LinearRegression().fit(X_simple, y)
residuals = y - lr_simple.predict(X_simple)
axes[1,0].scatter(lr_simple.predict(X_simple), residuals)
axes[1,0].axhline(y=0, color='red', linestyle='--')
axes[1,0].set_xlabel('予測値')
axes[1,0].set_ylabel('残差')
axes[1,0].set_title('残差プロット')
# Q-Qプロット(正規性の確認)
from scipy import stats
stats.probplot(residuals, dist="norm", plot=axes[1,1])
axes[1,1].set_title('Q-Qプロット(残差の正規性)')
# 季節別の売上分布
df.boxplot(column='sales', by='season', ax=axes[1,2])
axes[1,2].set_title('季節別売上分布')
axes[1,2].set_xlabel('季節(0:春、1:夏、2:秋、3:冬)')
plt.tight_layout()
plt.show()
# 3. 重回帰分析
print(f"\n=== 重回帰分析 ===")
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import r2_score
from statsmodels.stats.outliers_influence import variance_inflation_factor
import statsmodels.api as sm
# 特徴量エンジニアリング
df['temp_squared'] = df['temperature'] ** 2 # 温度の二次項
df['ad_temp_interaction'] = df['advertising_cost'] * df['temperature'] # 交互作用項
# ダミー変数化(季節)
season_dummies = pd.get_dummies(df['season'], prefix='season')
df_extended = pd.concat([df, season_dummies], axis=1)
# 説明変数の選択
feature_cols = ['advertising_cost', 'temperature', 'temp_squared', 'competitors',
'ad_temp_interaction', 'season_1', 'season_2', 'season_3']
X = df_extended[feature_cols]
y = df_extended['sales']
# OLSによる詳細な回帰分析
X_with_const = sm.add_constant(X)
ols_model = sm.OLS(y, X_with_const).fit()
print("重回帰分析結果:")
print(ols_model.summary())
# 多重共線性の確認(VIF)
print(f"\n多重共線性チェック(VIF):")
vif_data = pd.DataFrame()
vif_data["Variable"] = X.columns
vif_data["VIF"] = [variance_inflation_factor(X.values, i) for i in range(len(X.columns))]
print(vif_data)
print("注:VIF > 10 の場合、多重共線性の可能性あり")
# 4. モデルの診断
print(f"\n=== モデル診断 ===")
predictions = ols_model.predict(X_with_const)
residuals = y - predictions
# 決定係数
r2 = r2_score(y, predictions)
adjusted_r2 = ols_model.rsquared_adj
print(f"決定係数(R²): {r2:.3f}")
print(f"調整済み決定係数: {adjusted_r2:.3f}")
print(f"RMSE: {np.sqrt(np.mean(residuals**2)):.2f}")
# Durbin-Watson統計量(自己相関の検定)
from statsmodels.stats.diagnostic import durbin_watson
dw_stat = durbin_watson(residuals)
print(f"Durbin-Watson統計量: {dw_stat:.3f}")
print("注:2に近いほど自己相関なし、0または4に近いと自己相関あり")
# Breusch-Pagan検定(等分散性の検定)
from statsmodels.stats.diagnostic import het_breuschpagan
bp_stat, bp_pvalue, _, _ = het_breuschpagan(residuals, X_with_const)
print(f"Breusch-Pagan検定: p値={bp_pvalue:.4f} ({'等分散性あり' if bp_pvalue > 0.05 else '不等分散性あり'})")
# 5. 予測と信頼区間
print(f"\n=== 予測例 ===")
# 新しいデータポイントでの予測
new_data = pd.DataFrame({
'advertising_cost': [200, 300, 100],
'temperature': [25, 15, 30],
'competitors': [2, 4, 1],
'season': [1, 3, 0] # 夏、冬、春
})
# 特徴量の作成
new_data['temp_squared'] = new_data['temperature'] ** 2
new_data['ad_temp_interaction'] = new_data['advertising_cost'] * new_data['temperature']
# ダミー変数化
new_season_dummies = pd.get_dummies(new_data['season'], prefix='season')
# 欠損するカテゴリを0で補完
for col in ['season_1', 'season_2', 'season_3']:
if col not in new_season_dummies.columns:
new_season_dummies[col] = 0
new_data_extended = pd.concat([new_data, new_season_dummies], axis=1)
X_new = new_data_extended[feature_cols]
X_new_with_const = sm.add_constant(X_new)
# 予測値と信頼区間
predictions_new = ols_model.predict(X_new_with_const)
pred_intervals = ols_model.get_prediction(X_new_with_const).conf_int()
for i, (pred, lower, upper) in enumerate(zip(predictions_new, pred_intervals[:, 0], pred_intervals[:, 1])):
print(f"ケース{i+1}: 予測売上 {pred:.1f} (95%信頼区間: {lower:.1f} - {upper:.1f})")
return ols_model, df_extended
# 実行
regression_model, extended_df = comprehensive_correlation_regression()
高度な分析手法
クラスタリング分析
クラスタリング分析は、類似したデータポイントをグループ化する手法です。顧客セグメンテーション、市場分析、商品分類などに広く活用されます。
実装例:
def comprehensive_clustering_analysis():
"""包括的なクラスタリング分析"""
# 顧客データの作成
np.random.seed(42)
n_customers = 1000
# 3つの明確なセグメントを持つ顧客データ
# セグメント1: 高価値顧客(高購買頻度、高単価)
seg1_size = 200
seg1_frequency = np.random.normal(15, 3, seg1_size) # 月間購買回数
seg1_monetary = np.random.normal(50000, 10000, seg1_size) # 平均購買金額
seg1_recency = np.random.exponential(5, seg1_size) # 最終購買からの日数
# セグメント2: 中価値顧客(中購買頻度、中単価)
seg2_size = 500
seg2_frequency = np.random.normal(7, 2, seg2_size)
seg2_monetary = np.random.normal(25000, 5000, seg2_size)
seg2_recency = np.random.exponential(15, seg2_size)
# セグメント3: 低価値顧客(低購買頻度、低単価)
seg3_size = 300
seg3_frequency = np.random.normal(2, 1, seg3_size)
seg3_monetary = np.random.normal(8000, 2000, seg3_size)
seg3_recency = np.random.exponential(30, seg3_size)
# データの統合
customer_data = pd.DataFrame({
'customer_id': range(n_customers),
'frequency': np.concatenate([seg1_frequency, seg2_frequency, seg3_frequency]),
'monetary': np.concatenate([seg1_monetary, seg2_monetary, seg3_monetary]),
'recency': np.concatenate([seg1_recency, seg2_recency, seg3_recency]),
'true_segment': [0]*seg1_size + [1]*seg2_size + [2]*seg3_size # 正解ラベル
})
# 負の値を調整
customer_data['frequency'] = np.maximum(0, customer_data['frequency'])
customer_data['monetary'] = np.maximum(1000, customer_data['monetary'])
customer_data['recency'] = np.maximum(1, customer_data['recency'])
print("=== 顧客セグメンテーション分析 ===")
print(f"総顧客数: {len(customer_data)}人")
print("\n基本統計量:")
print(customer_data[['frequency', 'monetary', 'recency']].describe())
# 1. データの前処理
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans, AgglomerativeClustering, DBSCAN
from sklearn.metrics import silhouette_score, adjusted_rand_score, calinski_harabasz_score
# 特徴量の標準化
features = ['frequency', 'monetary', 'recency']
scaler = StandardScaler()
X_scaled = scaler.fit_transform(customer_data[features])
# 2. 最適クラスタ数の決定
print(f"\n=== 最適クラスタ数の決定 ===")
# エルボー法とシルエット分析
max_clusters = 10
inertias = []
silhouette_scores = []
for k in range(2, max_clusters + 1):
kmeans = KMeans(n_clusters=k, random_state=42, n_init=10)
labels = kmeans.fit_predict(X_scaled)
inertias.append(kmeans.inertia_)
sil_score = silhouette_score(X_scaled, labels)
silhouette_scores.append(sil_score)
print(f"k={k}: 慣性={kmeans.inertia_:.2f}, シルエット係数={sil_score:.3f}")
# 可視化
fig, axes = plt.subplots(1, 3, figsize=(18, 5))
# エルボー法
axes[0].plot(range(2, max_clusters + 1), inertias, 'bo-')
axes[0].set_xlabel('クラスタ数')
axes[0].set_ylabel('慣性(WCSS)')
axes[0].set_title('エルボー法')
# シルエット係数
axes[1].plot(range(2, max_clusters + 1), silhouette_scores, 'ro-')
axes[1].set_xlabel('クラスタ数')
axes[1].set_ylabel('シルエット係数')
axes[1].set_title('シルエット分析')
# 最適なクラスタ数を選択(シルエット係数が最大)
optimal_k = range(2, max_clusters + 1)[np.argmax(silhouette_scores)]
print(f"\n推奨クラスタ数: {optimal_k}")
# 3. 複数のクラスタリング手法の比較
clustering_methods = {
'K-means': KMeans(n_clusters=optimal_k, random_state=42, n_init=10),
'Hierarchical': AgglomerativeClustering(n_clusters=optimal_k),
'DBSCAN': DBSCAN(eps=0.5, min_samples=5)
}
results = {}
for method_name, method in clustering_methods.items():
labels = method.fit_predict(X_scaled)
if len(np.unique(labels)) > 1: # クラスタが複数ある場合のみ
sil_score = silhouette_score(X_scaled, labels)
ch_score = calinski_harabasz_score(X_scaled, labels)
ari_score = adjusted_rand_score(customer_data['true_segment'], labels)
else:
sil_score = ch_score = ari_score = -1
results[method_name] = {
'labels': labels,
'n_clusters': len(np.unique(labels)),
'silhouette': sil_score,
'calinski_harabasz': ch_score,
'adjusted_rand_index': ari_score
}
print(f"\n{method_name}:")
print(f" クラスタ数: {results[method_name]['n_clusters']}")
print(f" シルエット係数: {sil_score:.3f}")
print(f" Calinski-Harabasz指数: {ch_score:.2f}")
print(f" 調整ランド指数(正解との比較): {ari_score:.3f}")
# 4. K-meansの詳細分析
best_kmeans = KMeans(n_clusters=optimal_k, random_state=42, n_init=10)
kmeans_labels = best_kmeans.fit_predict(X_scaled)
customer_data['cluster'] = kmeans_labels
# クラスタ別の特徴分析
print(f"\n=== クラスタ別特徴分析(K-means、k={optimal_k}) ===")
cluster_summary = customer_data.groupby('cluster')[features].agg(['mean', 'std', 'count'])
print(cluster_summary.round(2))
# クラスタの解釈
cluster_interpretations = {}
for cluster_id in range(optimal_k):
cluster_data = customer_data[customer_data['cluster'] == cluster_id]
avg_freq = cluster_data['frequency'].mean()
avg_monetary = cluster_data['monetary'].mean()
avg_recency = cluster_data['recency'].mean()
# 特徴に基づく解釈
if avg_freq > 10 and avg_monetary > 30000:
interpretation = "VIP顧客(高頻度・高単価)"
elif avg_freq > 5 and avg_monetary > 15000:
interpretation = "優良顧客(中頻度・中単価)"
elif avg_recency > 20:
interpretation = "休眠顧客(低頻度・最近未購買)"
else:
interpretation = "一般顧客"
cluster_interpretations[cluster_id] = interpretation
print(f"\nクラスタ{cluster_id} ({interpretation}):")
print(f" 顧客数: {len(cluster_data)}人")
print(f" 平均購買頻度: {avg_freq:.1f}回/月")
print(f" 平均購買金額: {avg_monetary:,.0f}円")
print(f" 平均最終購買日数: {avg_recency:.1f}日前")
# 5. クラスタの可視化
from sklearn.decomposition import PCA
# PCAによる次元削減(可視化のため)
pca = PCA(n_components=2, random_state=42)
X_pca = pca.fit_transform(X_scaled)
# 可視化
axes[2].scatter(X_pca[:, 0], X_pca[:, 1], c=kmeans_labels, cmap='viridis', alpha=0.7)
axes[2].set_xlabel(f'第1主成分(寄与率: {pca.explained_variance_ratio_[0]:.1%})')
axes[2].set_ylabel(f'第2主成分(寄与率: {pca.explained_variance_ratio_[1]:.1%})')
axes[2].set_title('クラスタリング結果(PCA)')
# クラスタ中心の表示
centers_pca = pca.transform(best_kmeans.cluster_centers_)
axes[2].scatter(centers_pca[:, 0], centers_pca[:, 1],
c='red', marker='x', s=200, linewidths=3, label='クラスタ中心')
axes[2].legend()
plt.tight_layout()
plt.show()
# 6. ビジネス活用提案
print(f"\n=== ビジネス活用提案 ===")
for cluster_id, interpretation in cluster_interpretations.items():
cluster_size = len(customer_data[customer_data['cluster'] == cluster_id])
cluster_ratio = cluster_size / len(customer_data) * 100
print(f"\n{interpretation}(クラスタ{cluster_id})- {cluster_size}人({cluster_ratio:.1f}%):")
if "VIP" in interpretation:
print(" 推奨施策: VIP専用サービス、パーソナライズ商品提案")
elif "優良" in interpretation:
print(" 推奨施策: ロイヤリティプログラム、アップセル・クロスセル")
elif "休眠" in interpretation:
print(" 推奨施策: 復帰キャンペーン、特別オファー")
else:
print(" 推奨施策: エンゲージメント向上、購買頻度増加施策")
return customer_data, best_kmeans, cluster_interpretations
# 実行
customer_data, kmeans_model, interpretations = comprehensive_clustering_analysis()
時系列分析
時系列分析は、時間の経過とともに変化するデータのパターンを分析し、将来の値を予測する手法です。
実装例:
def comprehensive_time_series_analysis():
"""包括的な時系列分析"""
# より複雑な時系列データの作成
np.random.seed(42)
dates = pd.date_range(start='2020-01-01', end='2023-12-31', freq='D')
n_days = len(dates)
# 複数の成分を持つ時系列
# 1. トレンド成分
trend = np.linspace(1000, 1500, n_days)
# 2. 季節成分(年次・週次)
yearly_seasonal = 100 * np.sin(2 * np.pi * np.arange(n_days) / 365.25)
weekly_seasonal = 50 * np.sin(2 * np.pi * np.arange(n_days) / 7)
# 3. 不規則成分
noise = np.random.normal(0, 50, n_days)
# 4. 外部要因(イベント効果)
covid_effect = np.where((dates >= '2020-03-01') & (dates <= '2020-06-30'), -200, 0)
holiday_effect = np.where(dates.month == 12, 150, 0)
# 最終的な売上データ
sales = trend + yearly_seasonal + weekly_seasonal + noise + covid_effect + holiday_effect
sales = np.maximum(100, sales) # 最小値100
# データフレーム作成
ts_data = pd.DataFrame({
'date': dates,
'sales': sales,
'trend': trend,
'yearly_seasonal': yearly_seasonal,
'weekly_seasonal': weekly_seasonal,
'covid_effect': covid_effect,
'holiday_effect': holiday_effect
})
ts_data.set_index('date', inplace=True)
print("=== 時系列分析:売上データ ===")
print(f"期間: {ts_data.index.min()} - {ts_data.index.max()}")
print(f"データポイント数: {len(ts_data)}")
print(f"\n基本統計量:")
print(ts_data['sales'].describe())
# 1. 時系列の基本的な可視化
fig, axes = plt.subplots(3, 2, figsize=(15, 12))
# 元データ
axes[0,0].plot(ts_data.index, ts_data['sales'])
axes[0,0].set_title('売上の時系列推移')
axes[0,0].set_ylabel('売上')
# 月別集計
monthly_data = ts_data.resample('M')['sales'].sum()
axes[0,1].plot(monthly_data.index, monthly_data.values)
axes[0,1].set_title('月別売上推移')
axes[0,1].set_ylabel('月間売上')
# 2. 時系列分解
from statsmodels.tsa.seasonal import seasonal_decompose
# 加法モデルでの分解
decomposition = seasonal_decompose(ts_data['sales'], model='additive', period=365)
axes[1,0].plot(ts_data.index, decomposition.trend)
axes[1,0].set_title('トレンド成分')
axes[1,0].set_ylabel('トレンド')
axes[1,1].plot(ts_data.index, decomposition.seasonal[:365]) # 1年分のみ表示
axes[1,1].set_title('季節成分(1年分)')
axes[1,1].set_ylabel('季節効果')
axes[2,0].plot(ts_data.index, decomposition.resid)
axes[2,0].set_title('残差成分')
axes[2,0].set_ylabel('残差')
# 自己相関関数
from statsmodels.tsa.stattools import acf, pacf
# 自己相関
autocorr = acf(ts_data['sales'].dropna(), nlags=50)
axes[2,1].plot(range(len(autocorr)), autocorr)
axes[2,1].axhline(y=0, color='k', linestyle='-', alpha=0.3)
axes[2,1].axhline(y=0.05, color='r', linestyle='--', alpha=0.5)
axes[2,1].axhline(y=-0.05, color='r', linestyle='--', alpha=0.5)
axes[2,1].set_title('自己相関関数')
axes[2,1].set_ylabel('自己相関')
axes[2,1].set_xlabel('ラグ')
plt.tight_layout()
plt.show()
# 3. 定常性の検定
from statsmodels.tsa.stattools import adfuller, kpss
print(f"\n=== 定常性の検定 ===")
# ADF検定(帰無仮説:単位根あり=非定常)
adf_result = adfuller(ts_data['sales'].dropna())
print(f"ADF検定:")
print(f" 統計量: {adf_result[0]:.4f}")
print(f" p値: {adf_result[1]:.4f}")
print(f" 結果: {'定常' if adf_result[1] < 0.05 else '非定常'}(α=0.05)")
# KPSS検定(帰無仮説:定常)
kpss_result = kpss(ts_data['sales'].dropna())
print(f"\nKPSS検定:")
print(f" 統計量: {kpss_result[0]:.4f}")
print(f" p値: {kpss_result[1]:.4f}")
print(f" 結果: {'定常' if kpss_result[1] > 0.05 else '非定常'}(α=0.05)")
# 差分系列での定常化
ts_data['sales_diff'] = ts_data['sales'].diff()
# 差分系列の定常性確認
adf_diff = adfuller(ts_data['sales_diff'].dropna())
print(f"\n1次差分系列のADF検定:")
print(f" p値: {adf_diff[1]:.4f}")
print(f" 結果: {'定常' if adf_diff[1] < 0.05 else '非定常'}")
# 4. 予測モデルの構築
print(f"\n=== 予測モデルの比較 ===")
# 学習・テストデータの分割(時系列なので順序を保持)
train_size = int(len(ts_data) * 0.8)
train_data = ts_data[:train_size]
test_data = ts_data[train_size:]
print(f"学習期間: {train_data.index.min()} - {train_data.index.max()}")
print(f"テスト期間: {test_data.index.min()} - {test_data.index.max()}")
# モデル1: ARIMA
from statsmodels.tsa.arima.model import ARIMA
from statsmodels.tsa.holtwinters import ExponentialSmoothing
from sklearn.metrics import mean_absolute_error, mean_squared_error
# ARIMAの次数を自動選択
import warnings
warnings.filterwarnings('ignore')
# 簡略化:手動で次数設定
arima_model = ARIMA(train_data['sales'], order=(1, 1, 1), seasonal_order=(1, 1, 1, 7))
arima_fitted = arima_model.fit()
arima_forecast = arima_fitted.forecast(steps=len(test_data))
arima_mae = mean_absolute_error(test_data['sales'], arima_forecast)
arima_rmse = np.sqrt(mean_squared_error(test_data['sales'], arima_forecast))
print(f"\nARIMA(1,1,1)×(1,1,1,7):")
print(f" MAE: {arima_mae:.2f}")
print(f" RMSE: {arima_rmse:.2f}")
# モデル2: 指数平滑法
exp_smooth_model = ExponentialSmoothing(
train_data['sales'],
trend='add',
seasonal='add',
seasonal_periods=7
).fit()
exp_smooth_forecast = exp_smooth_model.forecast(len(test_data))
exp_smooth_mae = mean_absolute_error(test_data['sales'], exp_smooth_forecast)
exp_smooth_rmse = np.sqrt(mean_squared_error(test_data['sales'], exp_smooth_forecast))
print(f"\n指数平滑法(加法モデル):")
print(f" MAE: {exp_smooth_mae:.2f}")
print(f" RMSE: {exp_smooth_rmse:.2f}")
# モデル3: 単純な移動平均
window_size = 7
ma_forecast = train_data['sales'].rolling(window=window_size).mean().iloc[-1]
ma_forecast_series = pd.Series([ma_forecast] * len(test_data), index=test_data.index)
ma_mae = mean_absolute_error(test_data['sales'], ma_forecast_series)
ma_rmse = np.sqrt(mean_squared_error(test_data['sales'], ma_forecast_series))
print(f"\n移動平均({window_size}日):")
print(f" MAE: {ma_mae:.2f}")
print(f" RMSE: {ma_rmse:.2f}")
# 5. 予測結果の可視化
plt.figure(figsize=(15, 8))
# 学習データ
plt.plot(train_data.index[-100:], train_data['sales'].iloc[-100:],
label='学習データ', color='blue', alpha=0.7)
# 実際のテストデータ
plt.plot(test_data.index, test_data['sales'],
label='実際の値', color='black', linewidth=2)
# 各モデルの予測
plt.plot(test_data.index, arima_forecast,
label=f'ARIMA予測 (MAE:{arima_mae:.1f})', color='red', linestyle='--')
plt.plot(test_data.index, exp_smooth_forecast,
label=f'指数平滑法予測 (MAE:{exp_smooth_mae:.1f})', color='green', linestyle='--')
plt.plot(test_data.index, ma_forecast_series,
label=f'移動平均予測 (MAE:{ma_mae:.1f})', color='orange', linestyle='--')
plt.title('時系列予測結果の比較')
plt.xlabel('日付')
plt.ylabel('売上')
plt.legend()
plt.grid(True, alpha=0.3)
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
# 6. 予測精度の詳細分析
print(f"\n=== 予測精度の詳細分析 ===")
# 週別・月別での精度確認
test_data_copy = test_data.copy()
test_data_copy['arima_pred'] = arima_forecast
test_data_copy['exp_smooth_pred'] = exp_smooth_forecast
test_data_copy['ma_pred'] = ma_forecast_series
# 週別精度
test_data_copy['week'] = test_data_copy.index.isocalendar().week
weekly_accuracy = test_data_copy.groupby('week').apply(
lambda x: pd.Series({
'arima_mae': mean_absolute_error(x['sales'], x['arima_pred']),
'exp_smooth_mae': mean_absolute_error(x['sales'], x['exp_smooth_pred']),
'ma_mae': mean_absolute_error(x['sales'], x['ma_pred'])
})
)
print("週別予測精度(MAE):")
print(weekly_accuracy.round(2))
# 最適モデルの選択
model_comparison = {
'ARIMA': arima_mae,
'指数平滑法': exp_smooth_mae,
'移動平均': ma_mae
}
best_model = min(model_comparison, key=model_comparison.get)
print(f"\n最も精度の高いモデル: {best_model} (MAE: {model_comparison[best_model]:.2f})")
return ts_data, arima_fitted, exp_smooth_model, model_comparison
# 実行
ts_data, arima_model, exp_model, comparison = comprehensive_time_series_analysis()
よくある質問と回答
Q1: どの分析手法を最初に学ぶべき?
A: 以下の順序での学習を推奨します。
基礎段階(1-2ヶ月):
- 記述統計: 平均、分散、分布の理解
- データ可視化: ヒストグラム、散布図、箱ひげ図
- 相関分析: 変数間の関係性把握
応用段階(2-4ヶ月): 4. 仮説検定: t検定、カイ二乗検定 5. 回帰分析: 線形回帰、重回帰分析 6. 分散分析: ANOVA、多重比較
実践段階(4-6ヶ月以降): 7. クラスタリング: 顧客セグメンテーション 8. 時系列分析: トレンド・季節性分析 9. 機械学習: 予測モデル構築
Q2: 分析手法選択の判断基準は?
A: 以下の要因を考慮して選択します。
データの性質による選択:
data_type_guide = {
"数値データ(連続値)": {
"1変数": ["ヒストグラム", "記述統計", "正規性検定"],
"2変数": ["散布図", "相関分析", "回帰分析"],
"多変数": ["主成分分析", "クラスタリング", "重回帰"]
},
"カテゴリカルデータ": {
"1変数": ["度数分布", "円グラフ", "棒グラフ"],
"2変数": ["クロス集計", "カイ二乗検定"],
"多変数": ["対応分析", "ロジスティック回帰"]
},
"時系列データ": {
"探索": ["時系列プロット", "季節分解"],
"予測": ["ARIMA", "指数平滑法", "機械学習"],
"異常検知": ["統計的プロセス管理", "変化点検出"]
}
}
目的による選択:
- 現状把握: 記述統計、可視化
- 要因分析: 相関分析、回帰分析、分散分析
- グループ化: クラスタリング、セグメンテーション
- 予測: 機械学習、時系列分析
- 仮説検証: 仮説検定、A/Bテスト
Q3: 分析結果の信頼性をどう確保する?
A: 以下の手順で信頼性を確保します。
1. データ品質の確認:
def data_quality_check(df):
"""データ品質チェック"""
quality_report = {
"欠損値": df.isnull().sum(),
"重複": df.duplicated().sum(),
"外れ値": {},
"データ型": df.dtypes
}
# 外れ値検出(数値列のみ)
numeric_cols = df.select_dtypes(include=[np.number]).columns
for col in numeric_cols:
Q1 = df[col].quantile(0.25)
Q3 = df[col].quantile(0.75)
IQR = Q3 - Q1
outliers = df[(df[col] < Q1 - 1.5*IQR) | (df[col] > Q3 + 1.5*IQR)]
quality_report["外れ値"][col] = len(outliers)
return quality_report
2. 統計的妥当性の確認:
- サンプルサイズ: 統計的検出力の確保
- 前提条件: 正規性、等分散性、独立性の確認
- 多重検定: Bonferroni補正等での調整
- 効果量: 統計的有意性だけでなく実質的意味の評価
3. 交差検証・ロバストネス:
- 時系列: 時間分割での検証
- クロスバリデーション: k-fold、Leave-one-out
- 感度分析: パラメータ変更時の結果安定性
まとめ
データ分析手法は多岐にわたりますが、目的とデータの性質に応じて適切に選択することで、価値ある洞察を得ることができます。
データ分析成功の鍵
1. 体系的な手法理解: ✅ 各手法の適用場面を明確に把握
✅ データの種類と分析目的の対応関係理解
✅ 基礎から応用への段階的スキルアップ
✅ 統計的妥当性の常時確認
2. 実践的なアプローチ: ✅ ビジネス課題起点での分析設計
✅ 複数手法の組み合わせ活用
✅ 結果の解釈と行動への変換
✅ 継続的な分析スキル向上
3. 品質重視の分析プロセス: ✅ データ品質の事前確認
✅ 適切な前提条件の検証
✅ 結果の妥当性・再現性確保
✅ ステークホルダーへの分かりやすい説明
次のアクション
データ分析手法の理解が深まったら、以下の記事で実践力を向上させましょう:
- Python データ分析 初心者: 実装スキルの習得
- 統計学 基礎: 理論的基盤の強化
- 機械学習 アルゴリズム: 予測モデルの構築技術
最も重要なのは、理論と実践を両輪として学習を進めることです。各手法の特徴を理解した上で、実際のデータで試行錯誤することにより、データから価値ある洞察を導出する力が身につきます。分析力を武器に、ビジネス課題解決に貢献していきましょう。