ビジネスの現場でデータ分析を行う際、「要因をどう扱うか」が重要になる。
説明変数の中には、自社で制御できるもの(施策・活動) もあれば、制御できないが予測は可能なもの(トレンドや季節性)、さらに制御も予測も難しいもの(競合の動き) も存在する。
- どの変数が統計的に有意か(p値による検証)
- 要因のつながりを整理する(パス解析)
- 自社の施策を変えた場合どうなるか(介入シミュレーション)
といった観点で、「因果関係を読み解き、意思決定に活かせる形にすること」が重要になる。
今回扱う分析は、単回帰と重回帰が組み合わさった形 になっています。
- Education → Income
- 1つの説明変数で目的変数を説明する典型的な 単回帰 のケース。
- Income, Education → Health
- 複数の説明変数を用いて目的変数を説明する 重回帰 のケース。
本記事でのパス解析は、
「単回帰でシンプルな因果を確認し、その後、重回帰によって複数要因が絡む因果を整理する」
という流れになっています。
単回帰と重回帰を組み合わせることで、因果関係をより立体的に捉えられるようになります。
本記事では、Python(scikit-learn)を用いて、p値付き回帰分析からパス解析、さらに介入シミュレーションまでを実装します。
ノートを通じて得られる学び
- 機械学習の回帰分析を統計的に補強する方法
- 因果関係を意識したパス解析の考え方
- 介入シミュレーションを通じた「データで未来を予測する」
モジュールの読み込み
- データ分析・回帰モデル・可視化に必要なライブラリを読み込み。
- Ridge は scikit-learn の線形回帰モデルの一種で、正則化付きの回帰分析ができる。
import pandas as pd
import numpy as np
import scipy.stats as stats
from sklearn.linear_model import Ridge
import matplotlib.pyplot as plt
import seaborn as sns
データセットの読み込み
- CSVデータを pandas で読み込み
- 最初の数行を確
- 分析対象は「教育(Education)」「収入(Income)」「健康(Health)」を含むデータセット
df = pd.read_csv('data12_quantitative_health.csv')
print(df.head())
p値の計算用の関数
scikit-learn の回帰モデルは係数や切片は出せるが、p値を標準で計算できない。
そのため下記関数を使用しまとめて出力
- 係数(coeffs)
- 標準誤差(std_err)
- t値(t_value)
- p値(p_value)
※統計的な有意性をチェックするための補助ツールです。
'''
p値の計算
- 引数 :
- 学習済みモデル : model
- 説明変数 : X
- 目的変数 : y
- 戻値 :
- p値 : p_values
- 係数: coeffs
- 標準誤差: std_err
- t値: t_values
'''
def calculate_p_values(model, X, y):
params = np.append(model.intercept_, model.coef_)
predictions = model.predict(X)
newX = pd.DataFrame({'Constant': np.ones(len(X))}).join(pd.DataFrame(X.reset_index(drop=True)))
MSE = (sum((y.values.flatten()-predictions.flatten())**2)) / (len(newX) - len(newX.columns))
var_b = MSE * (np.linalg.inv(np.dot(newX.T, newX)).diagonal())
sd_b = np.sqrt(var_b)
ts_b = params / sd_b
columns = ['Constant'] + list(X.columns)
p_values = [2 * (1 - stats.t.cdf(np.abs(i), (len(newX) - 1))) for i in ts_b]
return pd.DataFrame({'Parameter': columns, 'coeffs': params, 'std_err': sd_b, 't_value': ts_b, 'p_value': p_values})
- scikit-learn の回帰モデル(例: LinearRegression, Ridge など)に対して、統計的な p値 を計算する関数
- scikit-learn の回帰モデルは「係数(coef_)や切片(intercept_)」は出してくれますが、t値やp値は標準では出力されない
パス解析
- Education → Income
- Education, Income → Health
このパス図に基づいて、それぞれの回帰モデルを実装していきます。
パス解析(1つ目のパス)
Incomeが目的変数、Educationが説明変数。
「教育(Education)」が「収入(Income)」に与える影響をモデル化しています。
切片・係数・決定係数 (R²) を確認し、さらに calculate_p_values で有意性もチェックします。
# 説明変数と目的変数
X = df[['Education']]
y = df[['Income']]
# 線形回帰モデルの作成
income_model = Ridge(alpha=0.001)
income_model.fit(X, y)
# モデルの係数と切片
print(f"切片: {income_model.intercept_}")
print(f"係数: {income_model.coef_}")
# 決定係数 (R^2) の出力
decision_coefficient = income_model.score(X, y)
print(f"決定係数 (R^2): {decision_coefficient}")
# p値
p_values_df = calculate_p_values(income_model, X, y)
print(p_values_df)
パス解析(2つ目のパス)
Healthが目的変数、EducationとIncomeが説明変数。
- 「教育(Education)」と「収入(Income)」が「健康(Health)」に与える影響を分析
- 係数・決定係数・p値を確認し、どの変数が有意に寄与しているかを見極める。
# 説明変数と目的変数
X = df[['Income','Education']]
y = df[['Health']]
# 線形回帰モデルの作成
health_model = Ridge(alpha=0.001)
health_model.fit(X, y)
# モデルの係数と切片
print(f"切片: {health_model.intercept_}")
print(f"係数: {health_model.coef_}")
# 予測
y_pred = health_model.predict(X)
# 決定係数 (R^2) の出力
decision_coefficient = health_model.score(X, y)
print(f"決定係数 (R^2): {decision_coefficient}")
# p値
p_values_df = calculate_p_values(health_model, X, y)
print(p_values_df)
介入
- 「教育年数を2年間延ばしたら、収入や健康にどう影響するか?」をシミュレーションするための介入処理。
- 因果推論でよく使われる「もし教育が増えたら…?」というシナリオ分析を行う
(介入の流れ)
- Educationを+2に設定
- Educationが+2になった場合のIncomeを予測
- Educationが+2になった場合Incomeを前提にHealthを予測
EducationからIncomeを予測
- Educationのデータに+2に設定。
-
income_model(一つ目の線形回帰モデル)``df_intervened['Education']にインプットして、predict(予測)する。
-incomeを予測する
# 介入:Educationの値を全員に対して増加させる
df_intervened = df.copy()
df_intervened['Education'] = df['Education'] + 2
# 介入後の予測
predicted_income = income_model.predict(df_intervened[['Education']])
df_intervened['Income'] = predicted_income
EducationとIncome(予測値)からHealthを予測
-
health_model(2つ目の線形回帰モデル)``df_intervened[['Income', 'Education']]の2つの説明変数をインプットして、predict(予測)する。
-Healthを予測する
# 介入後の予測
predicted_post_intervention = health_model.predict(df_intervened[['Income', 'Education']])
元データと介入シナリオとの比較
- 介入前と介入後の「健康(Health)」の分布を比較。
- 教育を伸ばした場合、平均的に健康がどのように変化するかを確認。
- 可視化(KDEプロット)を使うことで、青(介入前)とオレンジ(介入後)の分布の違いを直感的に把握。
# 結果の比較
```comparison = pd.DataFrame({
'Pre-Intervention': df['Health'].values,
'Post-Intervention': predicted_post_intervention.flatten()
})
print(comparison.describe())
# 分布の可視化
sns.kdeplot(
comparison['Pre-Intervention'],
label='Pre-Intervention',
fill=True,
bw_adjust=2
)
sns.kdeplot(
comparison['Post-Intervention'],
label='Post-Intervention',
fill=True,
bw_adjust=2
)
plt.legend()
plt.title('Health Distribution Before and After Intervention')
plt.xlabel('Health')
plt.ylabel('Density')
plt.show()
(出力結果)
ヒストグラムで確認すると、分布に変化が発生。
重要性のポイント
-
モデル精度だけでは不十分
- 機械学習や回帰モデルは「予測の当たりやすさ」を出してくれる
- でも実務では「その結果に意味があるのか?」まで考えないと、施策につながらない
- 有意性(p値チェック)が必要
-
相関と因果は違う
- 「変数AとBに相関がある」=「AがBの原因」ではない
- パス解析のように因果関係を整理してこそ、正しい意思決定につながる
- 因果整理の重要性
-
意思決定に直結する「介入」
- 実務で最も知りたいのは「施策を変えたらどうなるか」
- 介入シミュレーションで「教育を増やしたら健康が改善する」などの未来を試算できる
- データ分析が「施策立案の武器」になる
- 実務で最も知りたいのは「施策を変えたらどうなるか」
まとめて言うと
本記事では以下を実装
- scikit-learnで不足しているp値を補う関数の作成
- 回帰モデルを用いたパス解析の実装
- 教育施策を仮想的に変えた場合の介入シミュレーション
データ分析は単にモデルを作るだけでなく、有意性を確認し、因果を整理し、介入の効果をシミュレーションすることで意思決定に活かせる








