論文中に 「重回帰分析」として登場する用語。
重回帰分析 とは:複数の説明変数で目的変数を予測。各βは「他変数を一定にしたとき」のその変数の純粋な効果。
重回帰分析(multiple regression)は、 複数の説明変数で目的変数を同時に予測する回帰モデル。 「死亡率に効く要因は何か?高齢化率、 病院数、 医療費、 求人倍率のうち、 真に効くのはどれ?」のような問いを扱えます。 本サイトの論文の多くが重回帰を中核に使います。
モデル式:$y = \beta_0 + \beta_1 x_1 + \beta_2 x_2 + \cdots + \beta_k x_k + \varepsilon$
核心アイデア:各係数 $\beta_j$ は「他の変数を全て固定した上で、 $x_j$ を1単位増やすと y がどう変わるか」を表します。 つまり、 単純な相関では見えなかった「純粋な効果」を取り出せる。
例:交絡の制御
単変量相関では「有効求人倍率と死亡率に r = +0.308 の正相関」と出ます。 でもこれは疑似相関で、 真の犯人は「高齢化率」。 重回帰で高齢化率も同時に投入すると、 求人倍率の偏回帰係数は β ≈ +0.03(p > 0.4 で非有意)と急減します。 「高齢化を制御後は、 求人倍率は死亡率に独立した影響を持たない」ことが判明。 これが重回帰の威力です。
変数選択の指針:(i) 経済理論・先行研究に基づいて選ぶ、 (ii) AIC、 BIC、 LASSO で自動選択、 (iii) n ≥ 10×(変数の数) の経験則。 n=47 なら 4-5変数までが安全。
結果の解釈ステップ:
標準化偏回帰係数 β を使えば、 単位の違う変数同士の影響度を直接比較できます(「1標準偏差動かしたら、 y は何標準偏差動くか」)。 論文では生の係数と標準化係数の両方を併記するのが理想。
重回帰は単回帰の一般化。 複数の説明変数 x₁, ..., xₚ を使って目的変数 y を予測:
$$ y = \beta_0 + \beta_1 x_1 + \beta_2 x_2 + \cdots + \beta_p x_p + \varepsilon $$
各説明変数の影響を独立に定量化できます。 「他の変数を一定にしたときの効果」を測れるのが重要なポイント。
重回帰での β_j は「他のすべての変数を一定にしたときに、 x_j が1単位増えると y がどれだけ変わるか」を表します。 単回帰の係数とは違うことに注意。
$$ \mathbf{y} = X \boldsymbol{\beta} + \boldsymbol{\varepsilon} $$
X は n×(p+1) の設計行列(最初の列が全1で切片用)。 OLS の解:
$$ \hat{\boldsymbol{\beta}} = (X^T X)^{-1} X^T \mathbf{y} $$
説明変数同士が強く相関していると、 係数の推定が不安定に。 兆候:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | import pandas as pd import statsmodels.api as sm import statsmodels.formula.api as smf df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = df[df['年度']==2023].dropna() ## R風の式(formula API) model = smf.ols('D2101 ~ A4101 + I3101 + Q5101 + D1101', data=df).fit() print(model.summary()) ## VIF(多重共線性チェック) from statsmodels.stats.outliers_influence import variance_inflation_factor X = sm.add_constant(df[['A4101', 'I3101', 'Q5101', 'D1101']]) vifs = [variance_inflation_factor(X.values, i) for i in range(X.shape[1])] print(f'VIFs: {dict(zip(X.columns, vifs))}') ## ロバスト標準誤差(不均一分散に対応) robust = smf.ols('D2101 ~ A4101 + I3101 + Q5101 + D1101', data=df).fit(cov_type='HC3') print(robust.summary()) ## 診断テスト from statsmodels.stats.diagnostic import het_breuschpagan bp = het_breuschpagan(model.resid, model.model.exog) print(f'Breusch-Pagan p={bp[1]:.3f}(>0.05 で等分散)') |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | from sklearn.linear_model import LinearRegression, Ridge, Lasso from sklearn.preprocessing import StandardScaler from sklearn.pipeline import Pipeline from sklearn.model_selection import cross_val_score, KFold X = df[['A4101', 'I3101', 'Q5101', 'D1101']].values y = df['D2101'].values ## 通常の OLS pipe_ols = Pipeline([('scale', StandardScaler()), ('lr', LinearRegression())]) scores = cross_val_score(pipe_ols, X, y, cv=KFold(5, shuffle=True, random_state=42), scoring='r2') print(f'OLS CV R² : {scores.mean():.3f} ± {scores.std():.3f}') ## Ridge 回帰(多重共線性対策) pipe_ridge = Pipeline([('scale', StandardScaler()), ('ridge', Ridge(alpha=1.0))]) scores_r = cross_val_score(pipe_ridge, X, y, cv=5, scoring='r2') print(f'Ridge CV R²: {scores_r.mean():.3f} ± {scores_r.std():.3f}') ## 標準化係数の取得 pipe_ols.fit(X, y) coefs = pipe_ols.named_steps['lr'].coef_ print(f'標準化係数: {dict(zip(["A4101","I3101","Q5101","D1101"], coefs.round(3)))}') |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | import numpy as np from scipy import stats, linalg ## 行列計算で β = (X'X)^{-1} X'y を直接求める X_design = np.column_stack([np.ones(len(df)), X]) beta = linalg.solve(X_design.T @ X_design, X_design.T @ y) print(f'β (手計算): {beta.round(4)}') ## 残差と標準誤差 residuals = y - X_design @ beta n, p = X_design.shape sigma2 = (residuals @ residuals) / (n - p) cov_beta = sigma2 * linalg.inv(X_design.T @ X_design) se_beta = np.sqrt(np.diag(cov_beta)) print(f'標準誤差: {se_beta.round(4)}') ## t 統計量 と p 値 t_stats = beta / se_beta p_values = 2 * (1 - stats.t.cdf(np.abs(t_stats), df=n-p)) print(f't 値: {t_stats.round(3)}') print(f'p 値: {p_values.round(4)}') ## 信頼区間(95%) t_crit = stats.t.ppf(0.975, df=n-p) ci_lower = beta - t_crit * se_beta ci_upper = beta + t_crit * se_beta |