論文中に 「残差」として登場する用語。
残差 とは:実測値 − モデル予測値。残差プロットの形でモデルの妥当性(線形性・等分散性)を診断する。
残差(residual)は、 「実測値 $y_i$ - モデル予測値 $\hat{y}_i$」の差。 モデルが捉えきれなかった「説明できないズレ」です。 回帰分析の診断の中核で、 これを見ずに結果を信じてはいけません。
OLSの4つの仮定(残差で診断):
残差プロットの読み方:
Q-Qプロット:残差が正規分布に従うかを視覚的に確認。 点が直線上にあれば正規性OK、 大きく逸脱するなら非正規。
Python:fitted = model.fittedvalues; residuals = model.resid。 散布図と Q-Qプロット(statsmodels.graphics.gofplots.qqplot)を必ず描く習慣を。
残差 e_i = y_i - ŷ_i は、 観測値とモデル予測値の差。 モデルで捉えきれなかったランダムな変動を表します。
| 種類 | 定義 | 用途 |
|---|---|---|
| 通常残差 | e_i = y_i - ŷ_i | 基本 |
| 標準化残差 | e_i / σ | スケールフリー |
| スチューデント化残差 | e_i / SE(e_i) | 外れ値検出 |
| PRESS残差 | e_i / (1-h_ii) | 予測精度評価 |
少数の極端な観測値が回帰結果を大きく動かすことがあります。 主要な指標:
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 27 | import statsmodels.api as sm import matplotlib.pyplot as plt from scipy import stats X = sm.add_constant(x) model = sm.OLS(y, X).fit() # 残差を取得 residuals = model.resid fitted = model.fittedvalues std_resid = model.get_influence().resid_studentized_internal # 残差プロット fig, axes = plt.subplots(2, 2, figsize=(12, 10)) axes[0, 0].scatter(fitted, residuals) axes[0, 0].axhline(0, color='red') axes[0, 0].set_title('Residuals vs Fitted') stats.probplot(residuals, plot=axes[0, 1]) axes[0, 1].set_title('Normal Q-Q') # Cook's distance inf = model.get_influence() cook = inf.cooks_distance[0] axes[1, 0].stem(cook) axes[1, 0].axhline(4/len(x), color='red') axes[1, 0].set_title("Cook's Distance") |
「回帰した、 R² も高い」で満足せず、 必ず残差プロットを描く。 多くの研究者は残差分析を省略し、 重大なモデル誤指定を見逃しています。
| 目的 | 1変数 | 2変数 | 多変量 |
|---|---|---|---|
| 記述 | 平均, 中央値, 分散 | 相関, 共分散 | PCA, 因子分析 |
| 可視化 | ヒストグラム, 箱ひげ | 散布図, ヒートマップ | 散布図行列, バイプロット |
| 予測 | 時系列モデル | 単回帰 | 重回帰, Ridge, LASSO |
| 分類 | ロジスティック回帰 | 判別分析 | SVM, RF, NN |
| グループ化 | 階級分け | 2次元クラスタリング | k-means, 階層クラスタリング |
| 検定 | 1標本t検定 | 2標本t検定, χ² | ANOVA, MANOVA |
| n | 推奨手法 |
|---|---|
| n < 10 | 記述統計のみ、 ノンパラ検定、 ベイズ統計 |
| 10 ≤ n < 30 | t検定, ブートストラップ, 単回帰 |
| 30 ≤ n < 200 | 重回帰, ANOVA, 階層クラスタリング |
| 200 ≤ n < 10000 | 複雑な回帰, RF, GBM, k-means |
| n ≥ 10000 | 深層学習, 大規模分散学習 |
| ライブラリ | 用途 |
|---|---|
| numpy | 数値計算の基礎、 行列演算 |
| pandas | データフレーム、 表操作 |
| scipy | 統計関数、 最適化、 線形代数 |
| statsmodels | 古典統計、 検定、 回帰分析の詳細 |
| scikit-learn | 機械学習、 前処理、 評価 |
| matplotlib | 基本可視化 |
| seaborn | 統計的可視化(高級) |
| plotly | インタラクティブ可視化 |
| xgboost / lightgbm | 勾配ブースティング |
| PyTorch / TensorFlow | 深層学習 |
このページで扱った概念を、 学習効率のためにまとめます。 これを毎日見ることで、 統計の基礎が体に染み込みます。
| 記号 | 意味 | 読み方 |
|---|---|---|
| μ | 母平均 | ミュー |
| σ | 母標準偏差 | シグマ |
| σ² | 母分散 | シグマ二乗 |
| x̄ | 標本平均 | エックスバー |
| s | 標本標準偏差 | エス |
| n | 標本サイズ | エヌ |
| p | p値、 比率 | ピー |
| α | 有意水準 | アルファ |
| β | 回帰係数、 第二種誤り率 | ベータ |
| r | 相関係数 | アール |
| R² | 決定係数 | アール二乗 |
| Σ | 総和記号、 共分散行列 | シグマ大文字 |
| N(μ, σ²) | 正規分布 | ノーマル ミュー シグマ二乗 |
| t(df) | t分布 | ティー |
| χ²(df) | カイ二乗分布 | カイ二乗 |
| F(d1, d2) | F分布 | エフ |
| H₀, H₁ | 帰無仮説、 対立仮説 | エイチゼロ、 エイチワン |
| E[X] | 期待値 | エクスペクタンス |
| Var(X) | 分散 | バリアンス |
| Cov(X, Y) | 共分散 | カバリアンス |
💡 統計学・データサイエンスは「記号の意味を理解する」ことが最初の壁。 各記号が何を表すか、 公式の中での役割を覚えてしまえば、 後はパターンの組合せで様々な手法が理解できます。
(CRISP-DM プロセスより)
| 分野 | 主要技術 | 代表ツール |
|---|---|---|
| 記述統計 | 要約量、 可視化 | pandas, matplotlib |
| 推測統計 | 検定、 信頼区間 | scipy.stats, statsmodels |
| 機械学習 | 予測、 分類、 クラスタリング | scikit-learn, XGBoost |
| 深層学習 | NN、 画像、 自然言語 | PyTorch, TensorFlow |
| 時系列 | ARIMA、 状態空間、 LSTM | statsmodels, prophet |
| 因果推論 | RCT、 IV、 DiD、 PSM | DoWhy, EconML |
| ベイズ統計 | MCMC、 変分推論 | PyMC, Stan |
| 最適化 | 線形/凸/離散最適化 | scipy.optimize, cvxpy |
これらは互いに深く関連します:
残差 がデータサイエンスの体系の中でどこに位置するかを、 3つの異なる視点で可視化します。 同じ情報でも見方を変えると気付きが変わります。
🌐 統計・データサイエンス › 関連・回帰 › 回帰 › 残差
中心の概念から放射状に、 前提・兄弟・発展形・応用先などの関係性を矢印で結びます。 横の繋がりを見るのに最適。 ノードをドラッグ、 ホイールでズーム、 クリックで遷移。
大きな円が小さな円を包含する Circle Packing 図。 「残差」は緑色でハイライト。
長方形を入れ子に分割した Treemap 図。 各分野の規模感を面積で比較。 「残差」は緑色でハイライト。
| マップ | 分かること | こんな時に見る |
|---|---|---|
| 🔗 関係マップ | 手法間の横の関係(前提→発展→応用) | 「次に何を学べばよい?」 学習順序の判断 |
| ⭕ 包含マップ | 分類体系の入れ子構造(上位⊃下位) | 「この手法はどんなジャンルに属する?」 |
| 🌳 ツリーマップ | 分野の規模比較(面積=ボリューム) | 「データサイエンス全体の俯瞰像」 |
💡 ジャストインタイム学習のヒント:3つの視点を行き来することで、 概念を多角的に理解できます。 包含マップやツリーマップはズーム/ドリルダウンで大分類から細部まで探索できます。
残差・残差診断の重要語をクイックアクセス:
47都道府県の現金給与総額を「製造品出荷額(log)」「人口集中度(log)」で説明する OLS を当てはめ、 残差診断 4 プロット(残差 vs フィット、 Q-Q、 scale-location、 leverage-Cook)を作る。
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | import numpy as np import pandas as pd import matplotlib.pyplot as plt import statsmodels.api as sm import statsmodels.formula.api as smf from scipy import stats df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', header=1) df.columns = [c.strip() for c in df.columns] df['log_mfg'] = np.log(df['製造品出荷額等']) df['log_pop'] = np.log(df['総人口']) res = smf.ols('現金給与総額 ~ log_mfg + log_pop', data=df).fit() print(res.summary()) infl = res.get_influence() sresid = infl.resid_studentized_internal fitted = res.fittedvalues leverage = infl.hat_matrix_diag cooks = infl.cooks_distance[0] fig, axes = plt.subplots(2, 2, figsize=(11, 9)) # (1) Residuals vs Fitted axes[0,0].scatter(fitted, res.resid) axes[0,0].axhline(0, ls='--', color='red') axes[0,0].set_xlabel('Fitted'); axes[0,0].set_ylabel('Residual') axes[0,0].set_title('Residuals vs Fitted') # (2) Normal Q-Q sm.qqplot(sresid, line='45', fit=True, ax=axes[0,1]) axes[0,1].set_title('Normal Q-Q') # (3) Scale-Location axes[1,0].scatter(fitted, np.sqrt(np.abs(sresid))) axes[1,0].set_xlabel('Fitted'); axes[1,0].set_ylabel('√|standardized residual|') axes[1,0].set_title('Scale-Location') # (4) Residuals vs Leverage axes[1,1].scatter(leverage, sresid) for i, (h, r, c) in enumerate(zip(leverage, sresid, cooks)): if c > 4/len(df): axes[1,1].annotate(df.iloc[i]['都道府県'], (h, r), fontsize=8) axes[1,1].set_xlabel('Leverage'); axes[1,1].set_ylabel('Std. residual') axes[1,1].set_title('Residuals vs Leverage') plt.tight_layout(); plt.savefig('residual_diag.png', dpi=140) |
典型的な観察例: Residual vs Fitted で東京・神奈川が右上に飛び出し、 Q-Q プロットの右裾も外れる。 Cook 距離も東京・神奈川が突出(> 4/n)。 つまり「賃金水準が高い大都市」は通常の OLS の仮定(等分散・線形)から外れており、 ロバスト回帰や層別モデルで補強する必要がある。
1 2 3 4 5 6 7 8 9 10 11 12 13 | from statsmodels.stats.diagnostic import ( het_breuschpagan, het_white, acorr_ljungbox) from statsmodels.stats.stattools import durbin_watson # 正規性 print('Shapiro:', stats.shapiro(res.resid)) # 不均一分散 bp = het_breuschpagan(res.resid, res.model.exog) print(f'Breusch-Pagan : LM={bp[0]:.2f}, p={bp[1]:.4f}') white = het_white(res.resid, res.model.exog) print(f'White : LM={white[0]:.2f}, p={white[1]:.4f}') # 自己相関(時系列ならば) print('Durbin-Watson :', durbin_watson(res.resid)) |
y のスケールが大きい観測ほど残差の絶対値が大きく見えるのは自然で、 これを「不均一分散だ」と判断するのは早計。 標準化残差(resid_studentized_internal)か外部スチューデント化残差(resid_studentized_external)を用いてスケールを除いた上で、 fitted 値との依存を見るのが正しい。 Breusch-Pagan / White の検定で形式的にも確認すること。 残差プロットは目視と検定を組み合わせて初めて意味を持つ。
Q-Q プロットは小サンプル(n < 30)では端点が大きく揺れるのが普通で、 「裾が反っているから非正規」と判断すると過検出になる。 中心極限定理により、 大標本では係数推定の頑健性は高い。 Shapiro-Wilk 検定や Kolmogorov-Smirnov 検定で形式的に確認し、 さらに「歪度・尖度」を数値で確認するのが安全。 残差の正規性は「予測区間の正確性」に効くが、 係数推定の不偏性には効かない点を分けて理解する。
Cook 距離は便利だが「全係数への影響を 1 つの数値に圧縮」しているため、 個別係数への影響は見えない。 DFBETAS は係数ごとの影響、 DFFITS は予測値への影響、 leverage は X 空間上の極端さを別々に評価する。 大規模データで影響点を特定するには、 これら 4 指標を組み合わせる必要がある。 Cook の閾値も「4/n」と「1」の 2 流派があり、 解釈が割れる。
時系列回帰では残差に自己相関が残ることが多く、 OLS の標準誤差が過小評価される。 Durbin-Watson 統計量(≈ 2 ならOK、 < 1.5 or > 2.5 で系列相関の疑い)、 Ljung-Box 検定、 残差の ACF プロットで必ず確認する。 系列相関があれば Newey-West の HAC 標準誤差、 もしくは AR(1) 誤差付き GLS(ARMAX)に切り替える。 残差独立は線形回帰の四大仮定の一つで、 違反の影響は深刻。
Residuals vs Fitted で U 字や逆 U 字のパターンが出るのは、 モデルが線形項だけでは捉えきれない非線形性のサイン。 これに対し「x² を追加する」だけで対応すると、 別の点で再び非線形パターンが出る。 根本対策は (a) 目的変数を log/sqrt 変換、 (b) 説明変数を spline / GAM で柔軟化、 (c) ツリー系モデルへ切り替え、 のいずれか。 「partial residual plot」「component-plus-residual plot」で個別変数の非線形性を確認する。
学習データの残差は「フィット誤差」であって、 未知データへの「予測誤差」より楽観的になる。 RMSE を残差で計算しても汎化性能は分からない。 予測誤差を見るには交差検証 / hold-out / OOB(RF)/ test split のいずれかが必須。 「残差で良いから予測も良い」は過学習を見落とす典型ミス。 残差は仮定診断、 予測誤差は汎化能力評価、 と目的を切り分ける。
1 2 3 4 | import statsmodels.api as sm import statsmodels.formula.api as smf res = smf.ols('y ~ x1 + x2', data=df).fit() fig = sm.graphics.influence_plot(res, criterion='cooks') |
1 2 3 4 5 6 | # 時系列モデル(ARIMA 等)には組込みの plot_diagnostics があるが、 # 線形回帰は自作する必要がある(上の SSDSE 計算例を参照) from statsmodels.graphics.regressionplots import ( plot_leverage_resid2, plot_partregress_grid) fig = plt.figure(figsize=(11, 8)) plot_partregress_grid(res, fig=fig) # 部分回帰プロット |
1 2 3 4 5 6 7 | from scipy import stats print('Shapiro-Wilk :', stats.shapiro(res.resid)) print('Anderson-Darling:', stats.anderson(res.resid, dist='norm')) print("D'Agostino K² :", stats.normaltest(res.resid)) print('Jarque-Bera :', stats.jarque_bera(res.resid)) print('Skewness :', stats.skew(res.resid)) print('Kurtosis :', stats.kurtosis(res.resid)) |
1 2 3 4 5 6 7 8 9 10 11 | from sklearn.linear_model import LinearRegression from sklearn.model_selection import cross_val_predict, KFold cv = KFold(n_splits=5, shuffle=True, random_state=0) y_pred = cross_val_predict(LinearRegression(), X, y, cv=cv) oof_resid = y - y_pred fig, ax = plt.subplots(1, 2, figsize=(11, 4.5)) ax[0].scatter(y_pred, oof_resid); ax[0].axhline(0, ls='--', color='red') ax[0].set_title('OOF Residuals') sm.qqplot(oof_resid, line='45', fit=True, ax=ax[1]) |
1 2 3 4 5 | from sklearn.linear_model import HuberRegressor, RANSACRegressor huber = HuberRegressor().fit(X, y) ransac = RANSACRegressor(random_state=0).fit(X, y) # 外れ値判定マスク(RANSAC のみ) inliers = ransac.inlier_mask_ |
本セクションは「残差」を 47都道府県データ(SSDSE-B-2026)で具体的に確認するための追加教材です。 例として課税対象所得の実測値 − 回帰直線による予測値を扱います。
残差を 47都道府県データで直感的に捉えるには、 まず「課税対象所得の実測値 − 回帰直線による予測値」を思い浮かべます。 東京都・大阪府・神奈川県のように総人口が大きい都道府県ほど、 課税対象所得や就業者数も大きくなる傾向があり、 こうしたデータの「形」を 残差 は要約します。
たとえば 47都道府県を散布図にすると、 右肩上がりの帯状にデータが並びます。 この「帯の傾き」「帯のばらつき」「帯から外れる外れ値」を表現する道具が、 ここで扱う 残差 だとイメージしてください。
SSDSE-B-2026 の 47都道府県データから、 「課税対象所得の実測値 − 回帰直線による予測値」を Python で再現します。 まず一行で読み込めるよう、 引数を直書きしたシンプル版を示します:
# 最小コード(直書き)
df = pd.read_csv('data/raw/SSDSE-B-2026.csv')
続いて、 列名はリポジトリ準拠(A1101 総人口、 A1102 男性人口、 D3201 課税対象所得、 等)の本番コードです。
import pandas as pd
import numpy as np
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', header=[0,1,2])
# 列名を 3 段ヘッダの最下段だけ採用(コード列: A1101, D3201 等)
df.columns = [c[-1] for c in df.columns]
# 2022 年の 47都道府県スナップショット
sub = df[df['年度コード'] == 2022].copy()
x = sub['A1101'].astype(float) # 総人口
y = sub['D3201'].astype(float) # 課税対象所得
# 残差の基礎統計
x_mean, y_mean = x.mean(), y.mean()
beta1 = ((x - x_mean) * (y - y_mean)).sum() / ((x - x_mean) ** 2).sum()
beta0 = y_mean - beta1 * x_mean
print(f'n = {len(x)}') # 47
print(f'beta1 = {beta1:,.4f}') # 傾き
print(f'beta0 = {beta0:,.4f}') # 切片
print(f'相関係数 = {x.corr(y):.4f}') # 0.95+ になる
# 残差・決定係数も計算
y_hat = beta0 + beta1 * x
resid = y - y_hat
ss_res = (resid ** 2).sum()
ss_tot = ((y - y_mean) ** 2).sum()
r2 = 1 - ss_res / ss_tot
print(f'R^2 = {r2:.4f}')
このコードを実行すると、 47都道府県データから 残差に関連する係数・指標が直接得られます。 SSDSE-B-2026 が手元にない場合は、 統計データ活用コンペティション公式ページからダウンロードしてください。