ビジネスで使える統計学実践ガイド:データから価値ある洞察を導く分析手法
はじめに
統計学は、データから意味のある洞察を得るための強力なツールです。しかし、多くのビジネスパーソンにとって統計学は「難しい数学」というイメージがあり、実際の業務で活用できていないのが現状です。
この記事では、ビジネス現場で実際に使える統計学の手法を、具体的な事例とPythonコードとともに実践的に解説します。
1. ビジネス統計学の基本概念
記述統計と推測統計
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from scipy import stats
import warnings
warnings.filterwarnings('ignore')
# サンプルデータ作成(売上データ)
np.random.seed(42)
sales_data = pd.DataFrame({
'月': range(1, 25),
'売上': np.random.normal(1000000, 200000, 24) + np.sin(np.arange(24) * 2 * np.pi / 12) * 100000,
'広告費': np.random.normal(100000, 20000, 24),
'店舗数': np.random.poisson(10, 24) + 5,
'季節': ['春', '春', '春', '夏', '夏', '夏', '秋', '秋', '秋', '冬', '冬', '冬'] * 2
})
# 記述統計
print("=== 記述統計 ===")
print(sales_data['売上'].describe())
print(f"\n変動係数: {sales_data['売上'].std() / sales_data['売上'].mean():.3f}")
print(f"歪度: {stats.skew(sales_data['売上']):.3f}")
print(f"尖度: {stats.kurtosis(sales_data['売上']):.3f}")
確率分布の理解
# 正規分布の可視化
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
# 1. 正規分布
x = np.linspace(-4, 4, 100)
axes[0,0].plot(x, stats.norm.pdf(x, 0, 1), label='標準正規分布')
axes[0,0].plot(x, stats.norm.pdf(x, 0, 0.5), label='σ=0.5')
axes[0,0].plot(x, stats.norm.pdf(x, 0, 2), label='σ=2')
axes[0,0].set_title('正規分布')
axes[0,0].legend()
# 2. t分布
axes[0,1].plot(x, stats.norm.pdf(x, 0, 1), label='正規分布')
axes[0,1].plot(x, stats.t.pdf(x, df=1), label='t分布(df=1)')
axes[0,1].plot(x, stats.t.pdf(x, df=5), label='t分布(df=5)')
axes[0,1].set_title('t分布')
axes[0,1].legend()
# 3. カイ二乗分布
x_chi = np.linspace(0, 15, 100)
axes[1,0].plot(x_chi, stats.chi2.pdf(x_chi, df=1), label='df=1')
axes[1,0].plot(x_chi, stats.chi2.pdf(x_chi, df=3), label='df=3')
axes[1,0].plot(x_chi, stats.chi2.pdf(x_chi, df=5), label='df=5')
axes[1,0].set_title('カイ二乗分布')
axes[1,0].legend()
# 4. F分布
x_f = np.linspace(0, 5, 100)
axes[1,1].plot(x_f, stats.f.pdf(x_f, dfn=2, dfd=10), label='F(2,10)')
axes[1,1].plot(x_f, stats.f.pdf(x_f, dfn=5, dfd=10), label='F(5,10)')
axes[1,1].set_title('F分布')
axes[1,1].legend()
plt.tight_layout()
plt.show()
2. 仮説検定の実践
t検定による平均値の比較
# A/Bテストの例:新しいマーケティング施策の効果検証
np.random.seed(42)
# グループA(従来施策)とグループB(新施策)のコンバージョン率
group_a = np.random.normal(0.05, 0.01, 1000) # 平均5%
group_b = np.random.normal(0.07, 0.01, 1000) # 平均7%
def perform_ab_test(group_a, group_b, alpha=0.05):
"""A/Bテストの実行"""
# 基本統計量
mean_a, std_a = np.mean(group_a), np.std(group_a, ddof=1)
mean_b, std_b = np.mean(group_b), np.std(group_b, ddof=1)
n_a, n_b = len(group_a), len(group_b)
print("=== A/Bテスト結果 ===")
print(f"グループA: 平均={mean_a:.4f}, 標準偏差={std_a:.4f}, n={n_a}")
print(f"グループB: 平均={mean_b:.4f}, 標準偏差={std_b:.4f}, n={n_b}")
# 等分散性の検定(F検定)
f_stat = std_a**2 / std_b**2 if std_a > std_b else std_b**2 / std_a**2
f_p_value = 2 * (1 - stats.f.cdf(f_stat, n_a-1, n_b-1))
print(f"\n等分散性検定: F統計量={f_stat:.4f}, p値={f_p_value:.4f}")
# t検定の実行
if f_p_value > alpha: # 等分散
t_stat, p_value = stats.ttest_ind(group_a, group_b, equal_var=True)
test_type = "等分散t検定"
else: # 不等分散
t_stat, p_value = stats.ttest_ind(group_a, group_b, equal_var=False)
test_type = "Welchのt検定"
print(f"\n{test_type}: t統計量={t_stat:.4f}, p値={p_value:.4f}")
# 効果量(Cohen's d)の計算
pooled_std = np.sqrt(((n_a-1)*std_a**2 + (n_b-1)*std_b**2) / (n_a+n_b-2))
cohens_d = (mean_b - mean_a) / pooled_std
print(f"効果量(Cohen's d): {cohens_d:.4f}")
# 結果の解釈
if p_value < alpha:
print(f"\n結論: 有意水準{alpha}で有意差あり(p < {alpha})")
print(f"新施策はコンバージョン率を{((mean_b-mean_a)/mean_a)*100:.1f}%改善")
else:
print(f"\n結論: 有意水準{alpha}で有意差なし(p >= {alpha})")
# 信頼区間の計算
diff_mean = mean_b - mean_a
se_diff = np.sqrt(std_a**2/n_a + std_b**2/n_b)
df = n_a + n_b - 2
t_critical = stats.t.ppf(1 - alpha/2, df)
ci_lower = diff_mean - t_critical * se_diff
ci_upper = diff_mean + t_critical * se_diff
print(f"平均差の{(1-alpha)*100}%信頼区間: [{ci_lower:.4f}, {ci_upper:.4f}]")
return {
'p_value': p_value,
'effect_size': cohens_d,
'confidence_interval': (ci_lower, ci_upper)
}
# A/Bテスト実行
result = perform_ab_test(group_a, group_b)
カイ二乗検定による独立性の検定
# 顧客セグメントと購買行動の関連性分析
def chi_square_independence_test():
"""カイ二乗独立性検定の実行"""
# クロス集計表の作成
observed = np.array([
[120, 80, 50], # 若年層:購入、検討中、非購入
[100, 120, 80], # 中年層
[60, 90, 150] # 高年層
])
row_labels = ['若年層', '中年層', '高年層']
col_labels = ['購入', '検討中', '非購入']
# 観測度数の表示
observed_df = pd.DataFrame(observed,
index=row_labels,
columns=col_labels)
print("=== 観測度数 ===")
print(observed_df)
# カイ二乗検定の実行
chi2_stat, p_value, dof, expected = stats.chi2_contingency(observed)
print(f"\n=== カイ二乗検定結果 ===")
print(f"カイ二乗統計量: {chi2_stat:.4f}")
print(f"p値: {p_value:.4f}")
print(f"自由度: {dof}")
# 期待度数の表示
expected_df = pd.DataFrame(expected,
index=row_labels,
columns=col_labels)
print(f"\n=== 期待度数 ===")
print(expected_df.round(2))
# 残差分析
residuals = (observed - expected) / np.sqrt(expected)
residuals_df = pd.DataFrame(residuals,
index=row_labels,
columns=col_labels)
print(f"\n=== 標準化残差 ===")
print(residuals_df.round(2))
# 結果の解釈
alpha = 0.05
if p_value < alpha:
print(f"\n結論: 有意水準{alpha}で独立性は棄却される(関連性あり)")
# どのセルが特に寄与しているかを確認
print("\n特に寄与の大きいセル(|標準化残差| > 2):")
for i, row in enumerate(row_labels):
for j, col in enumerate(col_labels):
if abs(residuals[i,j]) > 2:
direction = "多い" if residuals[i,j] > 0 else "少ない"
print(f"- {row}×{col}: 期待より{direction}(残差={residuals[i,j]:.2f})")
else:
print(f"\n結論: 有意水準{alpha}で独立性は棄却されない(関連性なし)")
# Cramér's V(効果量)の計算
n = observed.sum()
cramers_v = np.sqrt(chi2_stat / (n * (min(observed.shape) - 1)))
print(f"\nCramér's V(効果量): {cramers_v:.4f}")
return chi2_stat, p_value, cramers_v
chi_square_independence_test()
3. 回帰分析の実践
単回帰分析
# 広告費と売上の関係分析
def simple_regression_analysis():
"""単回帰分析の実行"""
# データ準備
advertising = sales_data['広告費']
sales = sales_data['売上']
# 回帰分析の実行
slope, intercept, r_value, p_value, std_err = stats.linregress(advertising, sales)
print("=== 単回帰分析結果 ===")
print(f"回帰式: 売上 = {intercept:.2f} + {slope:.2f} × 広告費")
print(f"相関係数: {r_value:.4f}")
print(f"決定係数: {r_value**2:.4f}")
print(f"p値: {p_value:.4f}")
print(f"標準誤差: {std_err:.4f}")
# 予測値の計算
predicted_sales = intercept + slope * advertising
residuals = sales - predicted_sales
# 残差分析
print(f"\n=== 残差分析 ===")
print(f"残差の平均: {np.mean(residuals):.2f}")
print(f"残差の標準偏差: {np.std(residuals):.2f}")
# 正規性検定(Shapiro-Wilk検定)
shapiro_stat, shapiro_p = stats.shapiro(residuals)
print(f"残差の正規性検定: W={shapiro_stat:.4f}, p={shapiro_p:.4f}")
# 可視化
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
# 散布図と回帰直線
axes[0,0].scatter(advertising, sales, alpha=0.6)
axes[0,0].plot(advertising, predicted_sales, 'r-', linewidth=2)
axes[0,0].set_xlabel('広告費')
axes[0,0].set_ylabel('売上')
axes[0,0].set_title('散布図と回帰直線')
# 残差プロット
axes[0,1].scatter(predicted_sales, residuals, alpha=0.6)
axes[0,1].axhline(y=0, color='r', linestyle='--')
axes[0,1].set_xlabel('予測値')
axes[0,1].set_ylabel('残差')
axes[0,1].set_title('残差プロット')
# Q-Qプロット
stats.probplot(residuals, dist="norm", plot=axes[1,0])
axes[1,0].set_title('Q-Qプロット(正規性確認)')
# 残差のヒストグラム
axes[1,1].hist(residuals, bins=10, alpha=0.7, density=True)
axes[1,1].set_xlabel('残差')
axes[1,1].set_ylabel('密度')
axes[1,1].set_title('残差の分布')
plt.tight_layout()
plt.show()
# 信頼区間の計算
n = len(advertising)
t_critical = stats.t.ppf(0.975, n-2) # 95%信頼区間
slope_ci = slope + np.array([-1, 1]) * t_critical * std_err
print(f"\n回帰係数の95%信頼区間: [{slope_ci[0]:.2f}, {slope_ci[1]:.2f}]")
return slope, intercept, r_value**2
slope, intercept, r_squared = simple_regression_analysis()
重回帰分析
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, r2_score
from sklearn.preprocessing import StandardScaler
def multiple_regression_analysis():
"""重回帰分析の実行"""
# 説明変数と目的変数の準備
X = sales_data[['広告費', '店舗数']]
y = sales_data['売上']
# ダミー変数の作成(季節)
season_dummies = pd.get_dummies(sales_data['季節'], prefix='季節')
X = pd.concat([X, season_dummies], axis=1)
print("=== 重回帰分析 ===")
print("説明変数:", X.columns.tolist())
# 回帰分析の実行
model = LinearRegression()
model.fit(X, y)
# 予測
y_pred = model.predict(X)
# 結果の表示
print(f"\n切片: {model.intercept_:.2f}")
print("回帰係数:")
for i, col in enumerate(X.columns):
print(f" {col}: {model.coef_[i]:.2f}")
# モデルの評価
r2 = r2_score(y, y_pred)
adjusted_r2 = 1 - (1 - r2) * (len(y) - 1) / (len(y) - X.shape[1] - 1)
rmse = np.sqrt(mean_squared_error(y, y_pred))
print(f"\n=== モデル評価 ===")
print(f"決定係数 (R²): {r2:.4f}")
print(f"調整済み決定係数: {adjusted_r2:.4f}")
print(f"RMSE: {rmse:.2f}")
# 各変数の重要度(標準化回帰係数)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
model_scaled = LinearRegression()
model_scaled.fit(X_scaled, y)
print(f"\n=== 標準化回帰係数(重要度) ===")
importance_df = pd.DataFrame({
'変数': X.columns,
'標準化係数': model_scaled.coef_,
'重要度': np.abs(model_scaled.coef_)
}).sort_values('重要度', ascending=False)
print(importance_df)
# 多重共線性の確認(VIF)
from statsmodels.stats.outliers_influence import variance_inflation_factor
vif_data = pd.DataFrame()
vif_data["変数"] = X.columns
vif_data["VIF"] = [variance_inflation_factor(X.values, i) for i in range(X.shape[1])]
print(f"\n=== 多重共線性診断(VIF) ===")
print(vif_data)
print("※ VIF > 10 の場合、多重共線性の可能性あり")
return model, X, y
model, X, y = multiple_regression_analysis()
4. 時系列分析の基礎
トレンド・季節性の分解
from statsmodels.tsa.seasonal import seasonal_decompose
from statsmodels.tsa.stattools import adfuller
def time_series_analysis():
"""時系列分析の実行"""
# 時系列データの準備
dates = pd.date_range('2023-01-01', periods=24, freq='M')
ts_data = pd.Series(sales_data['売上'].values, index=dates)
# 時系列分解
decomposition = seasonal_decompose(ts_data, model='additive', period=12)
# 可視化
fig, axes = plt.subplots(4, 1, figsize=(12, 10))
decomposition.observed.plot(ax=axes[0], title='元データ')
decomposition.trend.plot(ax=axes[1], title='トレンド')
decomposition.seasonal.plot(ax=axes[2], title='季節成分')
decomposition.resid.plot(ax=axes[3], title='残差')
plt.tight_layout()
plt.show()
# 定常性の検定(ADF検定)
adf_result = adfuller(ts_data.dropna())
print("=== 定常性検定(ADF検定) ===")
print(f"ADF統計量: {adf_result[0]:.4f}")
print(f"p値: {adf_result[1]:.4f}")
print(f"臨界値:")
for key, value in adf_result[4].items():
print(f" {key}: {value:.4f}")
if adf_result[1] < 0.05:
print("結論: 時系列は定常である(p < 0.05)")
else:
print("結論: 時系列は非定常である(p >= 0.05)")
return decomposition
decomposition = time_series_analysis()
5. 実践的な分析事例
顧客セグメンテーション分析
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
def customer_segmentation_analysis():
"""RFM分析によるセグメンテーション"""
# サンプル顧客データ作成
np.random.seed(42)
n_customers = 1000
customer_data = pd.DataFrame({
'customer_id': range(1, n_customers + 1),
'recency': np.random.exponential(30, n_customers), # 最終購入からの日数
'frequency': np.random.poisson(5, n_customers) + 1, # 購入回数
'monetary': np.random.lognormal(8, 1, n_customers) # 購入金額
})
# RFMスコアの計算
customer_data['R_score'] = pd.qcut(customer_data['recency'], 5, labels=[5,4,3,2,1])
customer_data['F_score'] = pd.qcut(customer_data['frequency'].rank(method='first'), 5, labels=[1,2,3,4,5])
customer_data['M_score'] = pd.qcut(customer_data['monetary'], 5, labels=[1,2,3,4,5])
# RFMスコアを数値に変換
customer_data['R_score'] = customer_data['R_score'].astype(int)
customer_data['F_score'] = customer_data['F_score'].astype(int)
customer_data['M_score'] = customer_data['M_score'].astype(int)
# 総合スコア
customer_data['RFM_score'] = (customer_data['R_score'].astype(str) +
customer_data['F_score'].astype(str) +
customer_data['M_score'].astype(str))
# クラスタリング
features = ['recency', 'frequency', 'monetary']
scaler = StandardScaler()
scaled_features = scaler.fit_transform(customer_data[features])
# 最適クラスタ数の決定(エルボー法)
inertias = []
k_range = range(2, 11)
for k in k_range:
kmeans = KMeans(n_clusters=k, random_state=42)
kmeans.fit(scaled_features)
inertias.append(kmeans.inertia_)
# エルボー法の可視化
plt.figure(figsize=(10, 6))
plt.plot(k_range, inertias, 'bo-')
plt.xlabel('クラスタ数')
plt.ylabel('慣性(Inertia)')
plt.title('エルボー法によるクラスタ数決定')
plt.show()
# 4クラスタでクラスタリング実行
kmeans = KMeans(n_clusters=4, random_state=42)
customer_data['cluster'] = kmeans.fit_predict(scaled_features)
# クラスタ別統計
cluster_summary = customer_data.groupby('cluster')[features].agg(['mean', 'std']).round(2)
print("=== クラスタ別統計 ===")
print(cluster_summary)
# クラスタの解釈
cluster_interpretation = {
0: "優良顧客",
1: "新規顧客",
2: "休眠顧客",
3: "一般顧客"
}
print(f"\n=== クラスタ解釈 ===")
for cluster_id in range(4):
cluster_data = customer_data[customer_data['cluster'] == cluster_id]
print(f"クラスタ {cluster_id} ({cluster_interpretation[cluster_id]}):")
print(f" 顧客数: {len(cluster_data)}人")
print(f" 平均Recency: {cluster_data['recency'].mean():.1f}日")
print(f" 平均Frequency: {cluster_data['frequency'].mean():.1f}回")
print(f" 平均Monetary: {cluster_data['monetary'].mean():.0f}円")
print()
return customer_data, kmeans
customer_data, kmeans_model = customer_segmentation_analysis()
まとめ
ビジネスで統計学を活用する際の重要なポイント:
実践のコツ:
- 目的の明確化:何を知りたいかを明確にする
- 適切な手法選択:データの性質に応じた分析手法の選択
- 前提条件の確認:各統計手法の前提条件を満たしているか確認
- 結果の解釈:統計的有意性と実務的意義の両方を考慮
よく使う分析手法:
- 記述統計:データの概要把握
- 仮説検定:施策効果の検証
- 回帰分析:要因分析と予測
- クラスタリング:セグメンテーション
これらの手法を適切に活用することで、データから価値ある洞察を得て、意思決定の質を向上させることができます。
次回は、機械学習アルゴリズムの選択と評価について詳しく解説します。
コメント