論文一覧に戻る 📚 用語解説(ジャストインタイム型データサイエンス教育)
決定係数 R²
Coefficient of Determination (R²)
目的変数の分散のうち、モデルが説明できる割合。0〜1で、1に近いほど「よく予測できる」モデル。
回帰モデル決定係数R二乗
📍 文脈💡 30秒結論📖 詳しく

📍 あなたが今見ているもの

🍰 まずはやさしく

データのばらつきを説明する道具です。

予測モデルがどれくらい信頼できるかを確認するために使います。

スマホの利用時間で成績を予想するような場面で使われます。

ここでは決定係数がどのような意味を持つか読みます。

論文中に 「決定係数 R²」として登場する用語。

決定係数 R² とは:目的変数の分散のうち、モデルが説明できる割合。0〜1で、1に近いほど「よく予測できる」モデル。

💡 30秒で分かる結論

🍰 まずはやさしく

予測の正確さを測るものさしです。

モデルがどれだけ正しく予測できるか知るために使います。

テストの勉強時間で点数を予想するときなどに役立ちます。

ここでは決定係数の定義と範囲について読みます。

📖 もっと詳しく

決定係数 R²(R-squared, coefficient of determination)は、 回帰モデルの「当てはまりの良さ」を測る最重要指標。 「目的変数の分散のうち、 モデルが何 % 説明できるか」を表します。

範囲:0 〜 1(0% 〜 100%)。 R² = 0.95 なら「モデルは y のばらつきの95%を説明」、 R² = 0.1 なら「ほとんど説明できていない」。

計算:$R^2 = 1 - SS_\text{res}/SS_\text{tot}$

単回帰の特殊例:$R^2 = r^2$(相関係数の2乗)。 だから「相関が強いほど予測精度も高い」が成り立つ。

致命的な注意:変数を増やせば必ず R² は上がる

意味のないランダムな変数を追加しても、 R² は決して下がらない(最悪、 同じ)。 だから「R² が高い = 良いモデル」とは限らない。 過学習(overfitting)を見抜けない指標。

解決策調整済み R² を併用。 説明変数の数で罰則を加えるので、 役に立たない変数を入れたら下がる。 モデル間比較にはこちらが標準。

領域による目安

絶対基準」より「比較基準」として使う。 同じデータの別モデル間で比べる。

🎮 触って理解する — 点を動かして R² を体感する

散布図の点をドラッグすると、 最小二乗法で回帰直線がリアルタイムに引き直され、 決定係数 R² がその場で計算されます。 何もない場所をクリック / タップすると点が増えます。 下の色分けバーは、 全変動 SST が「説明できた変動 SSR(緑)」と「説明できない残差変動 SSE(赤)」に分かれる様子を示します。 点を直線に沿わせると R² は 1 に近づき、 ばらけさせると 0 に落ちます。

決定係数
R² = —
ŷ = —
変動の分解(バーの全長 = SST)
■ SSR(説明) SSE(残差)■
SST(全変動)
SSR(回帰)
SSE(残差)
点の数 n
点を動かして遊んでみましょう。

体感ポイント①:R² = SSR/SST = 1 − SSE/SST。 右のバーで緑(SSR)が占める割合がそのまま R² です。 点を回帰直線にぴったり沿わせると赤(SSE)が消え、 R² → 1。 完全にばらけさせると緑が消え、 R² → 0(=「平均で予測するのと同程度」)になります。

体感ポイント②:外れ値の罠。 「外れ値の罠」プリセットは、 大多数の点に無関係な 1 点を遠くに置いた例。 たった 1 点が回帰直線と R² を大きく動かすのを確認できます(→ 残差の散布図で必ず目視を)。

体感ポイント③:R² だけでは分からないAnscombe I〜IV は Anscombe (1973) の有名な 4 データセットで、 どれも R² ≈ 0.67 とほぼ同一なのに形は全く違います(直線・曲線・外れ値 1 点・レバレッジ点)。 「R² が同じ=関係が同じ」ではないことを、 数字ではなくで確かめてください。

この体験と本文のつながり

  • 変動の分解:全変動 SST を「説明できた分 SSR」と「説明できない残差 SSE」に分けるのが R² の心臓部。 上のバーはこの恒等式 SST = SSR + SSE を可視化しています。
  • 相関との関係:説明変数が 1 つの単回帰では、 相関係数 r の 2 乗がちょうど R² に一致します(R² = r²)。 「強い相関」プリセットで r が大きいほど R² が 1 に近づくのを確認できます。
  • 限界①(単調増加):説明変数を増やすと訓練データの R² は必ず上がる(または不変)。 無意味な変数でも下がりません。 だから変数の数が違うモデルを比べるときは自由度調整済み R²を使います。
  • 限界②(高 R² ≠ 良モデル):外れ値・非線形・過学習があると、 高い R² でも「良いモデル」とは限りません。 R² はあてはまりの指標であって、 因果や汎化性能を保証しないことを、 Anscombe プリセットで体で覚えてください。

👁️ 直感 — R²は「説明できた分散の割合」

決定係数 R² は、 回帰モデルが目的変数のばらつきを何%説明できたかを表す指標:

$$ R^2 = 1 - \frac{SS_{\text{res}}}{SS_{\text{tot}}} = \frac{SS_{\text{reg}}}{SS_{\text{tot}}} $$

0〜1 の値。 1 に近いほど「モデルがデータをよく説明」、 0 ならば「平均よりマシでない」。

具体例: SSDSE-B-2026 で 2023 年の 47 都道府県について、総人口から出生数を線形回帰で予測する。 全分散 SStot ≒ 1.35×1010、 残差平方和 SSres ≒ 1.24×108。 R² = 1 - 1.24×108 / 1.35×1010 = 0.99。 「出生数の分散の 99% は総人口で説明できている」と読み、 残り 1% は出生率の地域差や測定誤差として残ります。 一方、年平均気温から消費支出を予測すると、 気温と支出の関係は単調でなく弱いため、 R² ≒ 0.05 まで落ちます。 これが「説明できる分散の比率」の実感です。

注意: R² は 「予測がどれだけ当たるか」「真の因果関係を捉えたか」 は別物です。 R²=0.99 でも、 出生数は人口規模の結果 (人口が多いほど生まれる子の絶対数も多い) であり、 少子化対策は『出生率』を動かす話で、 総人口を増やせばよいという政策には直結しません。 R² は あてはまり指標 として読み、 解釈は別途因果推論で行う、 という分業を心がけてください。

🎯 R² の値の解釈

解釈 分野例
0.9〜1.0非常に高い説明力物理学(運動方程式)
0.7〜0.9高い説明力工学(材料試験)
0.4〜0.7中程度経済学・社会学
0.1〜0.4低い説明力(ある程度実用可能)心理学・行動科学
< 0.1ほぼ説明できていない複雑系

🔧 調整済み R²

R² は変数を増やすほど単調に増加する欠点があります。 これを補正:

$$ R^2_{\text{adj}} = 1 - (1 - R^2) \cdot \frac{n - 1}{n - p - 1} $$

説明変数数 p が増えるほど補正項が大きくなり、 「本当に有効な変数」だけが採用される傾向に。 重回帰のモデル選択で使用。

🚧 R² の落とし穴

R² (決定係数) を回帰分析で使う際に頻発する誤用を、 SSDSE-B-2026 の都道府県データで起きうる例とともに列挙する。 R² の性質を誤解した解釈が、 そのまま政策提言・モデル選択ミスにつながる。

🐍 Python での R²

🎯 このコードでやること: SSDSE-B-2026 を読み込み、 47 都道府県の総人口(A1101)と出生数(A4101)の単回帰を実行する。 そのうえで決定係数 R² を「statsmodels」「sklearn.metrics.r2_score」「手計算 (1 - SS_res/SS_tot)」の 3 通りで算出し、 全て一致することを確認する。 調整済み R² も併記する。
📥 入力データ: data/raw/SSDSE-B-2026.csv(47 都道府県 × 2023 年度) SSDSE-B-2026 都道府県 A1101 (総人口) A4101 (出生数, 人) 2023 北海道 5,092,000 24,430 2023 東京都 14,086,000 86,348 2023 大阪府 8,763,000 55,292 2023 鳥取県 537,000 3,263 …(合計 47 行)
 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
import pandas as pd
import numpy as np
from sklearn.metrics import r2_score
import statsmodels.api as sm

# SSDSE-B-2026 を読み込み、 2023 年の 47 都道府県データを抽出
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]
x = df['A1101'].astype(float)       # 総人口(説明変数)
y = df['A4101'].astype(float)       # 出生数(目的変数)

# OLS 単回帰モデル: y = a + b*x を statsmodels で推定
X = sm.add_constant(x)
model = sm.OLS(y, X).fit()
y_pred = model.predict(X)

# R² を 3 通りで算出(一致を確認)
r2_sm = model.rsquared
r2_sk = r2_score(y, y_pred)
ss_res = ((y - y_pred) ** 2).sum()
ss_tot = ((y - y.mean()) ** 2).sum()
r2_manual = 1 - ss_res / ss_tot

print(f'切片 a   = {model.params["const"]:.3e}')
print(f'傾き b   = {model.params["A1101"]:.4f}')
print(f'R² (sm)  = {r2_sm:.4f}')
print(f'R² (sk)  = {r2_sk:.4f}')
print(f'R² (手)  = {r2_manual:.4f}')
print(f'調整済R² = {model.rsquared_adj:.4f}')
📤 実行例: 上記コードを実行すると次の出力が得られる 切片 a = -6.769e+02 傾き b = 0.0061 R² (sm) = 0.9909 R² (sk) = 0.9909 R² (手) = 0.9909 調整済R² = 0.9907 → 3 通りの計算が小数点 4 桁まで一致。 数式 1 - SS_res/SS_tot がライブラリと同じ結果を返すことを確認できる。
💬 読み方: R² = 0.9909 は「出生数の分散の 99.09% は『総人口』だけで説明できる」を意味する。 これは出生数がほぼ人口規模に比例するためで、 因果関係ではなく強い共変動を示すに過ぎない。 調整済 R² も 0.9907 でほぼ同じ — 説明変数 1 個なので自由度補正の影響は微小。 社会科学では R² > 0.3 でも実用的、 物理現象では R² > 0.95 を期待、 と分野基準で読み解くこと。

🚧 落とし穴と注意点

🔬 数式を言葉で読み解く

数式 $R^2 = 1 - SS_\text{res}/SS_\text{tot}$ を言葉で読み解くと: SS_tot は「目的変数 y の平均からの全変動」(誤差ゼロのときの基準)、 SS_res は「モデルで予測しきれずに残った変動」。 比率 SS_res/SS_tot は「説明できなかった分の割合」なので、 1 から引くと「説明できた割合」になる。 R²=0.9 は「y のばらつきの 90% をモデルが説明、 10% は誤差」と読む。

記号と読み下しを SSDSE-B-2026 の都道府県データ (総人口 A1101 → 出生数 という回帰を想定) で対応付けます:

記号読み方意味SSDSE-B-2026 での例
$y_i$ワイ・アイi 番目の観測値(実測)i 県の出生数(実値)
$\hat{y}_i$ワイ・ハット・アイi 番目のモデル予測値総人口から予測した出生数
$\bar{y}$ワイ・バーy の標本平均(「何も知らない」予測の基準)47 県の出生数の平均
$SS_{\text{tot}}$エス・エス・トット$\sum(y_i-\bar{y})^2$。 全変動(モデルが説明すべき総量)県別総生産が平均から離れる全変動
$SS_{\text{res}}$エス・エス・レジ$\sum(y_i-\hat{y}_i)^2$。 残差平方和(説明しきれなかった部分)予測と実測のズレの 2 乗和
$SS_{\text{reg}}$エス・エス・レグ$\sum(\hat{y}_i-\bar{y})^2$。 モデルが説明した変動$SS_{\text{tot}} - SS_{\text{res}}$ で計算
$R^2$アール・ツー(決定係数)$1 - SS_{\text{res}}/SS_{\text{tot}}$。 説明できた割合 (0〜1)人口で総生産の 95% を説明できる、 など

1 行要約:「分母 = y の動きの総量」、 「分子 = 予測しきれなかった残りの動き」、 「1 − 分子/分母 = 説明できた割合」。 R² が 0 なら「ただ平均値を予測したのと同じ」、 1 なら「完全予測」、 負なら「平均値より悪い予測」を意味します。

「R²」を深く理解する

R² の幾何学

R² は「y のばらつきのうち、 説明変数で『説明』できた部分」。 全分散 SS_tot = 回帰平方和 SS_reg + 残差平方和 SS_res、 そして R² = SS_reg / SS_tot。

非線形モデルでの R²

非線形モデルでは R² が負になりうる(平均より悪い予測)。 そのときは R² の代わりに RMSE や MAE で評価。

多変量での R²

📝 練習問題 — 理解度チェック

  1. この用語の基本定義を、 自分の言葉で説明できますか?
  2. この手法が使われる典型的なシナリオを3つ挙げられますか?
  3. この手法の前提条件・仮定を確認できますか?
  4. 結果を解釈する際の注意点は何ですか?
  5. 類似手法との違いを説明できますか?
  6. Python(または他言語)で実装できますか?
  7. SSDSE データで応用例を作成できますか?

📚 参考文献・さらなる学習

古典的教科書

実践書

オンラインリソース

💼 実務応用ガイド

データサイエンスプロジェクトでの位置づけ

  1. 探索的分析(EDA):基本統計量・可視化でデータを理解
  2. 前処理:標準化・正規化・欠損値処理
  3. モデリング:回帰・分類・クラスタリング
  4. 評価:CV、 指標計算、 統計的検定
  5. 解釈・報告:効果量・信頼区間・可視化

業界別ユースケース

📖 完全ガイド — 統計学習の参照表

分析の流れ — 8ステップ

  1. 問題定義:何を知りたいのか、 目的を明確に
  2. データ収集:信頼できるソースから(SSDSEなど公的データ)
  3. データクリーニング:欠損値、 外れ値、 入力ミスの確認
  4. 探索的分析(EDA):要約統計量、 ヒストグラム、 散布図
  5. 変数変換:標準化、 対数変換、 カテゴリのエンコード
  6. モデリング:適切な手法を選び、 学習
  7. 評価:CV、 指標、 統計的検定
  8. 解釈・報告:効果量、 信頼区間、 可視化

統計手法の選び方マトリクス

目的 1変数 2変数 多変量
記述平均, 中央値, 分散相関, 共分散PCA, 因子分析
可視化ヒストグラム, 箱ひげ散布図, ヒートマップ散布図行列, バイプロット
予測時系列モデル単回帰重回帰, Ridge, LASSO
分類ロジスティック回帰判別分析SVM, RF, NN
グループ化階級分け2次元クラスタリングk-means, 階層クラスタリング
検定1標本t検定2標本t検定, χ²ANOVA, MANOVA

サンプル数別の手法ガイド

n 推奨手法
n < 10記述統計のみ、 ノンパラ検定、 ベイズ統計
10 ≤ n < 30t検定, ブートストラップ, 単回帰
30 ≤ n < 200重回帰, ANOVA, 階層クラスタリング
200 ≤ n < 10000複雑な回帰, RF, GBM, k-means
n ≥ 10000深層学習, 大規模分散学習

Python 主要ライブラリ早見表

ライブラリ 用途
numpy数値計算の基礎、 行列演算
pandasデータフレーム、 表操作
scipy統計関数、 最適化、 線形代数
statsmodels古典統計、 検定、 回帰分析の詳細
scikit-learn機械学習、 前処理、 評価
matplotlib基本可視化
seaborn統計的可視化(高級)
plotlyインタラクティブ可視化
xgboost / lightgbm勾配ブースティング
PyTorch / TensorFlow深層学習

よくある質問(FAQ)

📓 用語のまとめ — 30秒で理解

このページで扱った概念を、 学習効率のためにまとめます。 これを毎日見ることで、 統計の基礎が体に染み込みます。

必ず押さえるべき記号

記号 意味 読み方
μ母平均ミュー
σ母標準偏差シグマ
σ²母分散シグマ二乗
標本平均エックスバー
s標本標準偏差エス
n標本サイズエヌ
pp値、 比率ピー
α有意水準アルファ
β回帰係数、 第二種誤り率ベータ
r相関係数アール
決定係数アール二乗
Σ総和記号、 共分散行列シグマ大文字
N(μ, σ²)正規分布ノーマル ミュー シグマ二乗
t(df)t分布ティー
χ²(df)カイ二乗分布カイ二乗
F(d1, d2)F分布エフ
H₀, H₁帰無仮説、 対立仮説エイチゼロ、 エイチワン
E[X]期待値エクスペクタンス
Var(X)分散バリアンス
Cov(X, Y)共分散カバリアンス

💡 統計学・データサイエンスは「記号の意味を理解する」ことが最初の壁。 各記号が何を表すか、 公式の中での役割を覚えてしまえば、 後はパターンの組合せで様々な手法が理解できます。

🌐 データサイエンス全体像での位置づけ

データサイエンスのワークフロー

  1. ビジネス理解:何を解決したいか
  2. データ理解:どんなデータがあるか
  3. データ準備:前処理、 特徴量エンジニアリング
  4. モデリング:手法選択、 学習
  5. 評価:性能、 解釈性、 ビジネス価値
  6. 展開:実装、 運用、 監視

(CRISP-DM プロセスより)

💡 この 6 工程はデータ分析全般の流れで、 この用語だけの話ではない。 決定係数は「評価」の工程。 ただし単独では過学習を見抜けないので、 交差検証と併用する

主要分野のマッピング

分野 主要技術 代表ツール
記述統計要約量、 可視化pandas, matplotlib
推測統計検定、 信頼区間scipy.stats, statsmodels
機械学習予測、 分類、 クラスタリングscikit-learn, XGBoost
深層学習NN、 画像、 自然言語PyTorch, TensorFlow
時系列ARIMA、 状態空間、 LSTMstatsmodels, prophet
因果推論RCT、 IV、 DiD、 PSMDoWhy, EconML
ベイズ統計MCMC、 変分推論PyMC, Stan
最適化線形/凸/離散最適化scipy.optimize, cvxpy

キャリアパス

💎 良いデータ分析のための10のコツ

  1. 必ず可視化から始める:散布図、 ヒストグラム、 箱ひげ図
  2. 外れ値を意識する:除く前にドメイン的に理解
  3. 仮定を確認する:正規性、 独立性、 等分散性
  4. サンプルサイズに見合う複雑性:n=10 で深層学習はしない
  5. 効果量も併記する:p値だけでは不十分
  6. 信頼区間で不確実性を示す:点推定だけでは誤解の元
  7. 多重比較を補正する:探索的解析でも誠実に
  8. ホールドアウト or CV で評価する:訓練データの精度は意味がない
  9. 解釈可能性も重視する:ブラックボックスより white-box
  10. 再現可能なコードを書く:random_seed、 バージョン管理

🔗 用語間の関係 — 統計概念のネットワーク

記述統計の基本セット

これらは互いに深く関連します:

推測統計の基本セット

回帰モデルファミリー

クラスタリング・次元削減ファミリー

検定ファミリー

🔖 キーワード索引(深掘り版)

論文・記事に登場する用語のリンクで該当箇所へジャンプ:

🧮 SSDSE 実値計算 ⚠️ 落とし穴 6選 🐍 Python バリエーション 🔗 関連用語 調整済み R² 疑似 R² アウトサンプル R² 予測 R² 決定係数 SST/SSE/SSR 過学習 回帰評価

🧮 SSDSE-B 実値計算例:「人口」を家計支出で予測した R² と限界

SSDSE-B-2026(47都道府県、 2023年)で、 都道府県人口(A1101)を家計の食品支出 3 項目(魚介・肉・野菜)で予測する。 単回帰と重回帰で R² がどう変わるか実際の数値で見ます。

📊 モデル比較(仮想的な実値例)

モデル 説明変数 調整済み R² AIC
M1:単回帰魚介のみ0.040.021234
M2:単回帰肉のみ0.210.191212
M3:重回帰魚介+肉+野菜0.280.231209
M4:M3+ランダム10変数13変数0.550.361218

💡 洞察:M4 のように意味のないランダム変数を10個追加すると R² は 0.28 → 0.55 へ大きく増えますが、 調整済み R²は 0.23 → 0.36 と増分が小さく、 AIC も悪化(1209 → 1218)。 「R² 単独では過学習を見抜けない」現実が確認できます。

📊 アウトサンプル R²(一般化された決定係数)

訓練データの R² と、 5-fold CV による外挿 R² は別物です。 47県のような小データでは:

⚠️ R² の落とし穴(深掘り版・6件)

① 変数を増やすほど R² は必ず上がる

OLS の数学的性質として、 説明変数を追加すれば R² は絶対に下がらない(最悪、 同じ)。 意味のない変数(コインの裏表など)を加えても R² は上がるので、 「R² が高い = 良いモデル」とは限らない。 必ず調整済み R²(自由度補正)か AIC/BIC を併用してモデル選択する。 さらに最重要なのはアウトサンプル R²(CV や ホールドアウト)で、 訓練 R² の楽観バイアスを除いた評価をすること。

② 「R² が高い = 因果関係」と誤解する

R² が 0.95 でも、 それは「相関の二乗が大きい」だけで、 因果関係を保証しません。 アイスクリーム売上から溺死者数を予測すれば R² が高くなりますが、 「アイスを売れば溺死者が出る」わけではない(夏という共通原因)。 R² は当てはまりであって因果効果ではない。 因果には DiD、 IV、 RDD、 因果フォレスト等の専用手法が必要。

③ 領域による R² の基準を知らない

物理科学(再現実験ベース)では R² > 0.95 を期待しますが、 社会科学では人間行動の複雑さから R² = 0.3 でも実用的、 金融時系列では R² = 0.05 でも極めて貴重です。 「R² が低い = 悪いモデル」と即断するのではなく、 そのドメインで何が標準的か、 ベースラインモデル(平均予測など)と比べてどれだけ改善しているかを判断する。

④ 非線形モデル / GLM で R² を素直に使う

OLS の R² は「線形回帰で SS_tot を SS_res と SS_reg に直交分解できる」前提に立ちます。 ロジスティック回帰やポアソン回帰など GLM では、 残差の和が 0 にならず、 R² の解釈が崩れる。 代わりに McFadden 疑似 R²Cox-Snell R²Nagelkerke R² を使う。 これらは「対数尤度の比」をもとに定義され、 OLS の R² より基準が低い(McFadden 0.2-0.4 で良いフィット)。

⑤ R² の値を異なるデータ間で比較する

R² は SS_tot の大きさに依存します。 つまり同じモデルでも、 y のばらつきが大きいデータほど R² が大きく見えやすい。 「業界 A での R² = 0.8 と業界 B での R² = 0.6 だから A モデルが優秀」とは言えない。 比較すべきは同じデータでの異なるモデル間か、 標準化された予測誤差(RMSE/y_std、 MAPE等)です。

⑥ 異常値が R² を歪めるのを見落とす

R² は二乗和に基づくので、 1〜2 個の極端な外れ値が R² を大きく変えます。 「R² = 0.9 だが、 1つの極端な観測を除くと R² = 0.3」というケースは現実によくある。 必ず散布図と残差プロットで個別の点を確認、 Cook's distance や leverage で影響力の大きい点を特定する。 ロバスト回帰(Huber、 LAD)を併用する手もあります。

🐍 Python 実装バリエーション

① scikit-learn の r2_score

🎯 解説: 重回帰モデル(説明変数 2 以上)の R² を計算し、 説明変数を増やしたときの当てはまり改善を確認する。 説明変数を追加すると R² は必ず増加(または維持)するため、 過学習に注意。
📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) A1303(65歳以上人口) A4101(出生数) L3221(消費支出(二人以上の世帯)) 北海道 5,092,000 1,681,000 24,430 296,888 東京都 14,086,000 3,205,000 86,348 341,320 沖縄県 1,468,000 350,000 12,549 251,222 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# ── この抜粋で使うデータを用意します ──
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]          # 2023 年の 47 都道府県
X = df[['A1101', 'A1303', 'L3221']].astype(float).values
y = df['A4101'].astype(float).values          # 出生数
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, random_state=42)

from sklearn.metrics import r2_score
from sklearn.linear_model import LinearRegression
model = LinearRegression().fit(X_train, y_train)
print(r2_score(y_test, model.predict(X_test)))  # テストの R²
print(model.score(X_train, y_train))  # 訓練の R²
📥 入力例: data/raw/SSDSE-B-2026.csv 説明変数: A1101(総人口), A1303(65歳以上人口), B4101(年平均気温) 目的変数: A4101(出生数)
📤 実行例: 0.9845606305690915 0.9954416082191915
💬 読み方: 1 行目がテスト R² = 0.9846、 2 行目が訓練 R² = 0.9954。 訓練の方が高いのは当然で、 差 0.011 が「学習データに合わせた分」の目安になる。 47 件を 7:3 に割ると テストは 15 件しかないので、 random_state を変えるとテスト R² は大きく振れる。 汎化性能を語るなら 1 回の分割ではなく交差検証を使うこと。

注意:r2_score は負の値もとり得る(ベースラインより悪い場合)。 sklearn 公式の定義では y の平均を予測するモデルを下回る場合は R² < 0 になります。

② statsmodels(調整済み R²、 検定統計量つき)

🎯 解説: scikit-learn の LinearRegression で計算した R² と、 sklearn.metrics の r2_score 関数で計算した R² が一致することを確認する。 検証用にもう一段、 「相関係数の 2 乗 r²」とも一致するかを単回帰で確認する。
1
2
3
4
import statsmodels.api as sm
res = sm.OLS(y, sm.add_constant(X)).fit()
print(res.rsquared, res.rsquared_adj)
print(res.aic, res.bic)  # モデル選択用
📥 入力例: data/raw/SSDSE-B-2026.csv X: A1101(総人口)47 都道府県 y: A4101(出生数)
📤 実行例: 0.9937817580076815 0.9933479271710082 818.1036632322639 825.5042536391041
💬 読み方: 1 行目が R² = 0.99378 と自由度調整済み R² = 0.99335。 説明変数 3 個・n=47 なので調整の効きは 0.0004 程度と小さい。 2 行目の AIC = 818.1 / BIC = 825.5 はそれ単体では意味を持たない相対指標で、 別のモデルの AIC/BIC とを比べて初めて使える。 BIC の方が大きいのは、 BIC がパラメータ数へより強い罰則(log n = log 47 ≈ 3.85 倍)を課すため。

③ scipy.stats.pearsonr — 単回帰なら相関²

単回帰なら $R^2 = r^2$。 scipy で相関を計算し、 2乗するだけで OK。

🎯 解説: SSDSE-B-2026 の総人口(A1101)を説明変数、 出生数(A4101)を目的変数として単回帰を実行し、 決定係数 R² で当てはまりを評価する。 R² は「目的変数の分散のうちモデルが説明できる割合」を表し、 0〜1 の範囲で 1 に近いほど良い当てはまり。
1
2
3
from scipy.stats import pearsonr
r, p = pearsonr(x, y)
print(f"単回帰の R² = {r**2:.3f}")
📥 入力例: data/raw/SSDSE-B-2026.csv 47 都道府県 × 説明変数 1(A1101), 目的変数(A4101) 東京都 14,086,000 → 出生数 86,348 人 鳥取県 537,000 → 出生数 3,263 人
📤 実行例: R² = 0.991 傾き b = 0.0061 切片 a = -677 → 人口が出生数の 99.1% を説明
💬 読み方: R² が 0.99 と極端に高い理由は、 出生数が「人口の規模」にほぼ比例するため。 ただし R² が高い=因果ではなく、 産業構造や生産性などの交絡要因に注意。 社会科学では R² > 0.3 でも実用的と見なされる。

④ クロスバリデーション R²

🎯 解説: 重回帰モデル(説明変数 2 以上)の R² を計算し、 説明変数を増やしたときの当てはまり改善を確認する。 説明変数を追加すると R² は必ず増加(または維持)するため、 過学習に注意。
1
2
3
4
from sklearn.model_selection import cross_val_score
scores = cross_val_score(LinearRegression(), X, y,
                          cv=5, scoring='r2')
print(scores.mean(), scores.std())
📥 入力例: data/raw/SSDSE-B-2026.csv 説明変数: A1101(総人口), A1303(65歳以上人口), B4101(年平均気温) 目的変数: A4101(出生数)
📤 実行例: 0.9390649780556005 0.03327340716141129
💬 読み方: 5 分割交差検証の R² は 平均 0.939、 標準偏差 0.033。 上の単一分割で得た 0.985 より低く、 かつ fold 間で 0.03 程度ばらつく。 「1 回の train_test_split で出した R² を信じてはいけない」ことが数字で見える。 同じ R² なら説明変数が少ないモデルが望ましい(オッカムの剃刀)。

⑤ McFadden 疑似 R²(GLM 用)

🎯 解説: scikit-learn の LinearRegression で計算した R² と、 sklearn.metrics の r2_score 関数で計算した R² が一致することを確認する。 検証用にもう一段、 「相関係数の 2 乗 r²」とも一致するかを単回帰で確認する。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# statsmodels GLM
# McFadden 疑似 R² はロジスティック回帰の指標なので、目的変数は 2 値にする。
# 上の y(出生数・連続値)のままだと対数尤度が nan になり R² も nan になる。
import numpy as np
aging = df['A1303'] / df['A1101'] * 100
y_bin = (aging >= aging.median()).astype(int).values   # 高齢化率が中央値以上か
X_bin = sm.add_constant(np.log(df[['A1101']].astype(float)))
res_full = sm.GLM(y_bin, X_bin, family=sm.families.Binomial()).fit()
res_null = sm.GLM(y_bin, np.ones((len(y_bin), 1)), family=sm.families.Binomial()).fit()
pseudo_r2 = 1 - res_full.llf / res_null.llf
print(f"McFadden R² = {pseudo_r2:.3f}")
📥 入力例: data/raw/SSDSE-B-2026.csv X: A1101(総人口)47 都道府県 y: A4101(出生数)
📤 実行例: McFadden R² = 0.291
💬 読み方: McFadden 疑似 R² = 0.291。 通常の R² と違い、 0.2〜0.4 で「当てはまりが良い」とされる(McFadden 自身の目安)ので、 線形回帰の R² と同じ物差しで読んではいけない。 目的変数は「高齢化率が中央値以上か」の 2 値、 説明変数は総人口の対数。 連続値の y のままだと対数尤度が nan になり指標が計算できない点に注意。

🗺️ 概念マップ — 3つの視点で体系を理解する

決定係数 R² 前提: 残差平方和 SSR 並列: 相関係数 r 発展: 自由度調整 R² 応用: 回帰モデル評価 対比: MSE / RMSE 統合: AIC / BIC

決定係数 R² がデータサイエンスの体系の中でどこに位置するかを、 3つの異なる視点で可視化します。 同じ情報でも見方を変えると気付きが変わります。

📍 体系階層のパス

🌐 統計・データサイエンス関連・回帰回帰

① 🔗 関係マップ — 「他の手法とどう繋がっているか」

中心に 決定係数 R² を置き、 そこから 残差・分散・最小二乗法・重回帰・平均・標準偏差 など 計 9 個の用語へ、 前提・兄弟・発展形といった関係を矢印で結んでいます。 線がどちら向きかを見れば、 先に読むべき用語あとから読む用語が分かります。 ノードはドラッグで動かせ、 ホイールでズーム、 クリックでその用語のページへ遷移します。

凡例:現在の用語上位カテゴリ兄弟(並列)前提発展形応用先2階層先

② ⭕ 包含マップ — 「どのカテゴリに含まれているか」

大きな円が小さな円を包含する Circle Packing 図。 「決定係数 R²」は緑色でハイライト

📍現在地:統計・データサイエンス

③ 🌳 ツリーマップ — 「面積で見るボリューム比較」

長方形を入れ子に分割した Treemap 図。 各分野の規模感を面積で比較。 「決定係数 R²」は緑色でハイライト

🎯 3つのマップの使い分け

マップ 分かること こんな時に見る
🔗 関係マップ手法間の横の関係(前提→発展→応用)「次に何を学べばよい?」 学習順序の判断
⭕ 包含マップ分類体系の入れ子構造(上位⊃下位)「この手法はどんなジャンルに属する?」
🌳 ツリーマップ分野の規模比較(面積=ボリューム)「データサイエンス全体の俯瞰像」

💡 ジャストインタイム学習のヒント:3つの視点を行き来することで、 概念を多角的に理解できます。 包含マップやツリーマップはズーム/ドリルダウンで大分類から細部まで探索できます。

📌 補足セクション — 決定係数を SSDSE-B-2026 で確かめる

本セクションは「決定係数」を 47都道府県データ(SSDSE-B-2026)で具体的に確認するための追加教材です。 例として総人口で出生数を説明したときの R²を扱います。

🎨 直感で掴む — 決定係数

🍰 まずはやさしく

データの並び方を要約するメジャーのようなものです。

予測の線にデータがどれだけ集まっているかを見るために使います。

部活の練習量と試合結果の関係をグラフにしたときなどを想像してください。

ここでは決定係数を直感的に理解するための例を読みます。

決定係数を 47都道府県データで直感的に捉えるには、 まず「総人口で出生数を説明したときの R²」を思い浮かべます。 東京都・大阪府・神奈川県のように総人口が大きい都道府県ほど、 出生数や就業者数も大きくなる傾向があり、 こうしたデータの「形」を 決定係数 は要約します。

たとえば 47都道府県を散布図にすると、 右肩上がりの帯状にデータが並びます。 この「帯の傾き」「帯のばらつき」「帯から外れる外れ値」を表現する道具が、 ここで扱う 決定係数 だとイメージしてください。

R² = 0 〜 1 の数直線でイメージ: R²=0 はモデルが y の平均 ȳ と同じ予測しかできない状態 (= 説明できる分散がゼロ)、 R²=1 は全データ点が回帰直線上に乗る理想状態。 SSDSE-B-2026 の総人口 → 出生数回帰では R² ≈ 0.99 程度になり、 「総人口で出生数の分散の 99% を説明できる」と読み替える。 残り 1% は出生率・年齢構成といった人口規模以外の要因にあたる。

R² が低いときの直感: 散布図で「右肩上がりの線が引けない」「外れ値が多数ある」「データ点が雲のように散らばっている」状態。 例えば SSDSE-B の総人口で出生数を説明すれば R² は高いが、 総人口で粗出生率 (= A4101/A1101) を説明すれば R² は 0.03 前後まで急落する。 「分母で割った率指標」は人口規模の影響が消えるため、 R² がガクッと下がるのは正常で、 「説明する変数を間違えていないか」を見直す合図になる。

📐 数式または定義

🍰 まずはやさしく

予測の良さを数字で表す計算式です。

ばらつきのうち何パーセントを説明できるか出すために使います。

買い物で使った金額から商品の数を予想する計算などに似ています。

ここでは決定係数を求めるための数式について読みます。

決定係数の中心的な数式は次のとおりです( SSDSE-B-2026 の 47 都道府県 \(n=47\) を想定):

$$ \hat{y}_i = \hat{\beta}_0 + \hat{\beta}_1 x_i, \quad i = 1, 2, \dots, 47 $$ $$ \hat{\beta}_1 = \frac{\sum_{i=1}^{47} (x_i - \bar{x})(y_i - \bar{y})}{\sum_{i=1}^{47} (x_i - \bar{x})^2}, \quad \hat{\beta}_0 = \bar{y} - \hat{\beta}_1 \bar{x} $$

ここで \(x_i\) は総人口、 \(y_i\) は出生数、 \(\bar{x}, \bar{y}\) はそれぞれの標本平均を表します。 決定係数の解釈は、 上式で得られる係数や残差から導かれます。

🧮 実値で計算してみる — 決定係数

SSDSE-B-2026 の 47都道府県データから、 「総人口で出生数を説明したときの R²」を Python で再現します。 まず一行で読み込めるよう、 引数を直書きしたシンプル版を示します:

📥 入力例(SSDSE-B-2026 全体:564 行 × 112 列 = 47 都道府県 × 2012〜2023 年) 年度 地域コード 都道府県 A1101(総人口) A1303(65歳以上人口) A4101(出生数) … 2023 R01000 北海道 5,092,000 1,681,000 24,430 … 2023 R13000 東京都 14,086,000 3,205,000 86,348 … 2023 R47000 沖縄県 1,468,000 350,000 12,549 … …(残り 112 列は住宅・家計・教育・医療など)
1
2
# 最小コード(直書き, SSDSE は cp932・2 段ヘッダ)
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])

続いて、 列名はリポジトリ準拠(A1101 総人口、 A1102 日本人人口、 A4101 出生数、 等)の本番コードです。

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) A4101(出生数) 北海道 5,092,000 24,430 東京都 14,086,000 86,348 沖縄県 1,468,000 12,549 …(全 47 行)
 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
import pandas as pd
import numpy as np

# SSDSE は 2 段ヘッダ(1段目=コード, 2段目=日本語名)。 skiprows=[1] でコード名を採用
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])

# 2023 年の 47都道府県スナップショット
sub = df[df['SSDSE-B-2026'] == 2023].copy()
x = sub['A1101'].astype(float)   # 総人口
y = sub['A4101'].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}')     # 傾き ≈ 0.0061
print(f'beta0 = {beta0:,.4f}')     # 切片 ≈ -676.9
print(f'相関係数 = {x.corr(y):.4f}')  # ≈ 0.9954

# 残差・決定係数も計算
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 が手元にない場合は、 統計データ活用コンペティション公式ページからダウンロードしてください。

⚠️ 補足の落とし穴

🔗 関連用語(補足リンク)

相関係数 最小二乗法 残差 決定係数 共分散 p 値 標準誤差 多重共線性

🖼 概念図で押さえる決定係数 R²

R² は「全変動のうち、 モデルが説明できた割合」を示す。 ここでは 3 つの概念図で「回帰の幾何」「残差の意味」「過学習との関係」を整理する。

単回帰と R²
図 1:単回帰モデルと R² の幾何。 散布図上の点群に対し、 最小二乗法で直線をフィットすると、 各点から直線への垂直距離 (残差) の二乗和が最小になる。 R² = 1 − SSE/SST は「直線が捉えた変動 (SSR)」を「全変動 (SST)」で割った比。 R² = 0.9 なら「縦方向のばらつきの 90% が説明変数で説明できる」と読む。 ただし R² が高くても外挿は危険で、 必ず残差プロットで線形性を確認する。
残差プロットと R²
図 2:残差プロットによる R² の検証。 R² が高くても、 残差にパターン (例:U 字、 ファンシェイプ) があればモデルは不適切。 残差がランダムにゼロ付近に散らばっているのが「R² が信頼できる」状態。 Anscombe の四重奏のように、 R² が同じでもデータの形が全く違うケースは多い。 R² は「数字一つでモデル品質を判断できる」と誤解されがちだが、 必ず残差診断と組み合わせて使う。
多項式回帰と R²の過学習
図 3:高次多項式と R² の過学習。 説明変数を増やせば R² は必ず増加する (減少しない) という性質がある。 これが「高次多項式は R² が高いが汎化しない」過学習の根本原因。 対策として (a) 自由度ペナルティを掛けた「自由度調整済 R² (Adjusted R²)」を使う、 (b) Ridge / Lasso で正則化する、 (c) 交差検証で test set R² を評価する、 が標準。 SSDSE-B のような n=47 の小データでは特に Adjusted R² と CV-R² の両方を必ず報告すべき。

📌 図から読み取るポイント

🔬 R² を実データで多角的に確認する(R283 補強)

R² は数式 1 行で書ける一方、 「変数を増やすほど上がる」「外れ値で歪む」「非線形では負になる」など細かい落とし穴が多い。 ここでは SSDSE-B-2026 都道府県データを使い、 R² の挙動を 4 つの角度から確認する。

① 単回帰の R² を手計算 → scikit-learn と一致確認

このコードでやること: SSDSE-B-2026 の「総人口(A1101)」を説明変数、「出生数(A4101)」を目的変数として、 SS_tot / SS_res から R² を手計算し、 sklearn.metrics.r2_score と一致するかを検証する。

📥 入力: SSDSE-B-2026.csv(47都道府県、 数値カラム A1101, A4101)。 df.head() は「北海道 5092000 24430」のような行が並ぶ。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import pandas as pd
from sklearn.metrics import r2_score
from sklearn.linear_model import LinearRegression

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]
df = df.rename(columns={'A1101':'population', 'A4101':'births'})
X = df[['population']].values
y = df['births'].values
model = LinearRegression().fit(X, y)
y_hat = model.predict(X)

ss_res = ((y - y_hat)**2).sum()
ss_tot = ((y - y.mean())**2).sum()
r2_manual = 1 - ss_res/ss_tot
print(f'手計算 R²        = {r2_manual:.4f}')
print(f'sklearn r2_score = {r2_score(y, y_hat):.4f}')
print(f'model.score      = {model.score(X, y):.4f}')

📤 実行例:

手計算 R² = 0.9909 sklearn r2_score = 0.9909 model.score = 0.9909

💬 結果の読み方: 3 つの計算が完全一致 → 公式の理解が正しい。 R²=0.9909 は「出生数のばらつきの 99.09% を総人口だけで説明できた」ことを意味する。 ただし、 後述の通り外れ値の影響も点検すべき。

② 変数を増やすと R² が必ず上がる(過学習の罠)

このコードでやること: SSDSE-B-2026 の説明変数に「65歳以上人口」「年平均気温」を順に追加し、 R² が単調増加することを示す。 これが調整済み R² が必要な理由。

📥 入力: 同じ df。 説明変数を 1 → 2 → 3 個に増やしていく。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# ── この抜粋で使うデータを用意します(英字の項目コードで読み込み)──
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', header=0)
df = df[df['Code'].astype(str).str.match(r'^R\d{5}$', na=False)].copy()
for _c in df.columns[3:]:
    df[_c] = pd.to_numeric(df[_c], errors='coerce')
df = df[df['SSDSE-B-2026'] == df['SSDSE-B-2026'].max()]

from sklearn.linear_model import LinearRegression

# A1101=総人口, A1303=65歳以上人口, B4101=年平均気温
cols_sets = [
    ['A1101'],
    ['A1101', 'A1303'],
    ['A1101', 'A1303', 'B4101']
]
for cols in cols_sets:
    X = df[cols].astype(float).values
    r2 = LinearRegression().fit(X, df['A4101']).score(X, df['A4101'])
    n, k = len(df), len(cols)
    r2_adj = 1 - (1-r2)*(n-1)/(n-k-1)
    print(f'説明変数 {k} 個: R²={r2:.4f}  Adjusted R²={r2_adj:.4f}')

📤 実行例:

説明変数 1 個: R²=0.9909 Adjusted R²=0.9907 説明変数 2 個: R²=0.9930 Adjusted R²=0.9927 説明変数 3 個: R²=0.9930 Adjusted R²=0.9925

💬 結果の読み方: R² は 0.9909 → 0.9930 → 0.9930 と減ることはない(3 個目は 4 桁目まで同じで、 実質的に増えていない)。 一方 Adjusted R² は 0.9907 → 0.9927 → 0.9925 と下がる——3 個目の変数が自由度を 1 消費するだけで新しい情報を持たなかった、 ということ。 無関係な変数を足せば Adjusted R² は下がり「無駄な変数」と判定できる(→ 過学習の節)。 これがモデル比較に Adjusted R² を使う理由。

③ 外れ値「東京」の有無で R² がどう変わるか

このコードでやること: 東京を含む 47 都道府県と、 東京を除いた 46 都道府県で R² を比較し、 外れ値が R² を持ち上げている事実を視覚化する。

📥 入力: 同じ df。 「都道府県」または「Prefecture」カラムでフィルタ。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# ── この抜粋で使うデータを用意します(英字の項目コードで読み込み)──
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', header=0)
df = df[df['Code'].astype(str).str.match(r'^R\d{5}$', na=False)].copy()
for _c in df.columns[3:]:
    df[_c] = pd.to_numeric(df[_c], errors='coerce')
df = df[df['SSDSE-B-2026'] == df['SSDSE-B-2026'].max()]

from sklearn.linear_model import LinearRegression

df_no_tokyo = df[~df['Prefecture'].str.contains('東京', na=False)]

for label, d in [('全47都道府県', df), ('東京除く46', df_no_tokyo)]:
    X = d[['A1101']].astype(float).values
    r2 = LinearRegression().fit(X, d['A4101']).score(X, d['A4101'])
    print(f'{label}: R²={r2:.4f}  n={len(d)}')

📤 実行例:

全47都道府県: R²=0.9909 n=47 東京除く46: R²=0.9855 n=46

💬 結果の読み方: 東京を除くと R² は 0.9909 → 0.9855 とわずかに下がる。 東京は規模が大きいが回帰直線にほぼ乗るため、 単独で R² を大きく歪めてはいない。 とはいえ高い R² を見たら必ず外れ値の影響を点検する習慣を。

④ 非線形データへの線形回帰: R² は負になりうる

このコードでやること: SSDSE-B-2026 の「年平均気温」と「消費支出」のように線形関係が弱い組合せに線形回帰を当てはめ、 分布外予測では R² が負になりうることを確認する。

📥 入力: 同じ df。 説明変数 B4101(年平均気温)、 目的変数 L3221(消費支出)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score

# 年平均気温(B4101) から 消費支出(L3221) を線形回帰 — 関係は弱い
X = df[['B4101']].astype(float).values
y = df['L3221'].astype(float).values
r2_in = r2_score(y, LinearRegression().fit(X, y).predict(X))

# 寒い県で学習し、暖かい県で予測(分布外)→ R² は負になりうる
d = df.sort_values('B4101')
tr, te = d.iloc[:23], d.iloc[23:]
m = LinearRegression().fit(tr[['B4101']].astype(float), tr['L3221'].astype(float))
r2_out = m.score(te[['B4101']].astype(float), te['L3221'].astype(float))
print(f'当てはめ R² (全データ)  = {r2_in:.4f}')
print(f'分布外 R² (寒→暖 予測)  = {r2_out:.4f}')

📤 実行例:

当てはめ R² (全データ) = 0.0529 分布外 R² (寒→暖 予測) = -0.5093

💬 結果の読み方: 同じデータへの当てはめでは R²=0.05 と低いだけだが、 分布外(寒い県で学習→暖かい県で予測)では R²=-0.51 と負になる。 R² が負=「平均で予測したほうがマシ」。 R² の符号も常に確認する。

R² 4 つの実験のまとめ

🧮 数式に値を入れて手で計算する: 決定係数 R²

5 点の小サンプル (y, ŷ) から SSE / SST / R² を Step 1〜5 で計算し、 直後の Python コードが同じ値を返すか確認する。

Step 1: 観測値・予測値・平均

y = [3, 7, 12, 9, 4] ŷ = [3.8, 6.6, 12.3, 8.5, 4.8] (モデルの予測) ȳ = (3+7+12+9+4)/5 = 35/5 = 7.0

Step 2: SST = Σ(y - ȳ)²

(3-7)² = 16 (7-7)² = 0 (12-7)² = 25 (9-7)² = 4 (4-7)² = 9 SST = 16+0+25+4+9 = 54.0

Step 3: SSE = Σ(y - ŷ)²(残差二乗和)

(3 -3.8)² = (-0.8)² = 0.64 (7 -6.6)² = ( 0.4)² = 0.16 (12-12.3)² = (-0.3)² = 0.09 (9 -8.5)² = ( 0.5)² = 0.25 (4 -4.8)² = (-0.8)² = 0.64 SSE = 0.64+0.16+0.09+0.25+0.64 = 1.78

Step 4: R² = 1 - SSE/SST

R² = 1 - 1.78 / 54.0 = 1 - 0.03296 ≈ 0.967

Step 5: 参考に SSR = Σ(ŷ - ȳ)² も計算

(3.8-7)² = 10.24 (6.6-7)² = 0.16 (12.3-7)² = 28.09 (8.5-7)² = 2.25 (4.8-7)² = 4.84 SSR = 45.58 ※ ŷ は OLS フィット結果ではないため SSE + SSR = 47.36 ≠ SST。 R² は 1 - SSE/SST で定義した値 (≈0.967) が正解。

🐍 Python で再現

1
2
3
4
5
6
7
import numpy as np
y = np.array([3, 7, 12, 9, 4])
yhat = np.array([3.8, 6.6, 12.3, 8.5, 4.8])
sst = ((y - y.mean())**2).sum()
sse = ((y - yhat)**2).sum()
r2 = 1 - sse/sst
print(f"SST: {sst}, SSE: {sse:.2f}, R²: {r2:.3f}")

📤 実行結果

SST: 54.0, SSE: 1.78, R²: 0.967

💬 手計算 (Step 4) の R² ≈ 0.967 と Python 出力 (R² = 0.967) が完全一致。 SST=54.0 / SSE=1.78 も Step 2・3 と同じ値。

🔗 隣接手法への橋渡し

「決定係数 (R²)」は単独で完結する手法ではなく、 隣接領域と連携することで真価を発揮する。

SSDSE-B-2026 の総人口 → 出生数の単回帰で R² ≈ 0.99、 自由度調整済 R² も同等、 説明変数の追加 (65歳以上人口・年平均気温) で 0.995 まで上昇するが交差検証 R² で過学習を確認する流れが実用的。

🔖 キーワード索引 (補足)

決定係数 R²」は回帰モデルの当てはまりを示す代表指標。 SSDSE-B-2026 (47 都道府県) で総人口 → 出生数を回帰すると R² ≈ 0.99 となり、 「人口で出生数の 99% が説明できる」という解釈に対応する。 ただし R² は説明変数を増やすと単調に上昇するため、 自由度調整済 R² や交差検証 R² と併用する必要がある。

TSSRSSESS自由度調整済 R²交差検証 R²F 検定AIC / BIC過学習SSDSE-B-2026 単回帰

これらは「単純な R² の罠」を回避するための補正指標群である。

💡 30秒で分かる結論 (補足)

の補足ポイント:

📍 文脈ボックス (補足)

本ページの主目的は「SSDSE-B-2026 の県別 総人口・出生数・年齢別人口を題材に、 R² の数式・実値計算・落とし穴を一貫させる」ことである。 同じ R² でも、 単回帰と重回帰、 線形と非線形で意味が異なる点を強調する。

後段では TSS = ESS + RSS の分解を 47 都道府県の生データに適用し、 numpy と sklearn の r2_score が一致することを確認する流れになる。

🎨 直感で掴む

R² は「モデルがデータの散らばりを何割説明できたか」を 0〜1 で示します。 SSDSE-B-2026 の都道府県の総人口で出生数を予測すると R² ≈ 0.99、 つまり 99% の変動を人口だけで説明できます。 残り 1% は出生率の地域差など他要因が担っています。

「説明する」を分解する: 縦軸 y (出生数) を全 47 県でプロットすると、 平均線 ȳ の周りにばらつきが見えます。 この総ばらつき (TSS = Σ(y - ȳ)²) を、 直線 ŷ = β₀ + β₁ x で説明できた部分 (ESS = Σ(ŷ - ȳ)²) と、 残った部分 (RSS = Σ(y - ŷ)²) に分解し、 R² = ESS/TSS = 1 - RSS/TSS と定義します。 R² = 0.99 は「平均からの上下動 100% のうち、 99% を回帰直線が再現した」を意味します。

身近な比喩: 体重 (y) を身長 (x) だけで予測しようとすると R² ≈ 0.6 程度ですが、 年齢・性別・筋肉量を加えると R² ≈ 0.85 に上昇します。 「身長だけで体重の 60% を読める」、 「他要素を加えれば 85% 読める」と直感できれば R² は理解できたも同然です。

注意: R² = 1 は完全予測ですが、 過学習の可能性も高いです。 R² = 0 はモデルが平均線と同等で予測価値ゼロ、 R² < 0 は予測が平均線より 悪い 異常事態を意味します(OOS 検証で見られる)。 また R² は単調増加性質を持つため、 説明変数を増やすと必ず上昇 — そこで 調整済み R² が登場します。

📐 数式または定義

決定係数 $R^2$ は、 観測値 $y_i$ を予測値 $\hat{y}_i$ で近似したときの「説明できた変動の割合」として定義される:

$$ R^2 = 1 - \frac{\mathrm{RSS}}{\mathrm{TSS}} = 1 - \frac{\sum_{i=1}^n (y_i - \hat{y}_i)^2}{\sum_{i=1}^n (y_i - \bar{y})^2} $$

ここで TSS (Total Sum of Squares) は平均 $\bar{y}$ からの総変動、 RSS (Residual Sum of Squares) はモデル誤差の二乗和。 単回帰では同等の表現として $R^2 = \mathrm{ESS}/\mathrm{TSS}$ (ESS = Explained Sum of Squares = $\sum(\hat{y}_i - \bar{y})^2$) もよく使われ、 $\mathrm{TSS} = \mathrm{ESS} + \mathrm{RSS}$ の関係が成り立つ。 単回帰の場合は $R^2 = r_{xy}^2$ (Pearson 相関係数の二乗) でもあり、 ピアソン相関と密接に結びついている。

🔬 数式を言葉で読み解く

上式の各記号を SSDSE-B-2026 (総人口 → 出生数の単回帰) に当てはめて翻訳する。

分子の RSS は「予測モデルの誤差」、 分母の TSS は「予測しない (平均値だけ使う) ときの誤差」。 R² はこの比を 1 から引いた値で、 「予測モデルを使うことで残差をどれだけ減らせたか」のスコア。 $R^2 < 0$ なら「平均値より悪い予測」を意味し、 OOS (out-of-sample) 評価では時々発生する。

🧮 実値で計算してみる

R² (決定係数) を SSDSE-B-2026 の「総人口 (A1101) → 出生数 (A4101)」の単回帰で計算する。 全 47 都道府県データを使い、 R² = 1 - SS_res/SS_tot を手計算と Python の両方で求めて一致を確認する。

使用データ: SSDSE-B-2026 の x = A1101 (総人口, 人), y = A4101 (出生数, 人)。 単回帰 y = a + b·x をフィットして R² を算出。

Step操作値の確認
1y の平均 ȳ を計算ȳ ≈ 15,474 人
2SS_tot = Σ(y_i - ȳ)²≈ 1.35×10¹⁰
3単回帰の予測値 ŷ_i を計算b ≈ 0.0061, a ≈ -677
4SS_res = Σ(y_i - ŷ_i)²≈ 1.24×10⁸
5R² = 1 - SS_res/SS_tot1 - 0.0091 ≈ 0.9909

R² ≈ 0.9909 = 「出生数の分散のうち 99% は人口で説明できる」という強い当てはまり。 残り 1% が出生率の地域差などの説明変数で補強できる余地。

🐍 Python 実装

上記 Step 1-5 の R² 計算を sklearn と statsmodels で再現する。 SSDSE-B-2026 を読み込み、 単回帰モデルをフィットして R² を確認する。

🎯 このコードでやること: sklearn の LinearRegression と statsmodels の OLS で R² を計算し、 手計算値と一致することを確認する。 さらに自由度調整済 R² も併記する。

📥 入力データ: SSDSE-B-2026 の A1101 (総人口) と A4101 (出生数) 列、 47 都道府県分。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import pandas as pd
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]
X, y = df[['A1101']], df['A4101']                # 総人口 → 出生数
model = LinearRegression().fit(X, y)
r2 = r2_score(y, model.predict(X))
adj_r2 = 1 - (1-r2) * (47-1) / (47-1-1)
print(f'R²={r2:.4f}  Adjusted R²={adj_r2:.4f}  b={model.coef_[0]:.4f}  a={model.intercept_:.0f}')

📤 実行結果:

R²=0.9909 Adjusted R²=0.9907 b=0.0061 a=-677

💬 結果の読み方: R² = 0.9909 は手計算の 0.9909 と一致。 「総人口が 1 万人増えると出生数が約 61 人増える」傾き (b=0.0061) で、 47 都道府県の分散の 99% を説明。 Adjusted R² との差はわずかで、 単回帰なので過適合の懸念は小さい。

⚠ 落とし穴

🗺 概念マップ

決定係数 R² を中心に、 関連する概念・上位カテゴリ・応用領域を放射状に配置した。

決定係数 R² 前提: 回帰分析・最小二乗法 並列: 自由度調整済 R² 発展: AIC / BIC 応用: モデル比較・選択 対比: MSE / RMSE 統合: 多重共線性

🔗 隣接手法への橋渡し

「r squared」を中心に、 隣接する手法・概念との関係を以下に整理する。 単独の手法理解にとどまらず、 分析パイプライン全体での位置付けを把握することで、 適切な前段・後段・代替手法を選べる。

隣接手法関係接続のポイント
回帰分析全般上位 / 一般化R² は最小二乗法で当てはまった回帰モデルの当てはまり度を測る指標
自由度調整済み R²下位 / 特殊化重回帰で変数追加に対する penalty を加えた R²。 変数選択時はこちらを参照
RMSE / MAE並列 / 対比予測誤差を絶対量で測る。 R² は無次元、 RMSE は単位付き — 併記推奨
残差プロット作成前段 (検証準備)R² だけで判断せず、 残差の独立性・等分散性・正規性を視覚確認
交差検証 R²後段 (汎化評価)訓練時 R² は楽観的。 k-fold CV で out-of-sample R² を測る
AIC / BIC評価 / 比較モデル選択時は R² 単独でなく情報量規準で penalty 込み比較

決定係数 R² は「モデルの説明力」を一発で示す便利な指標だが、 単独で使うのは危険。 SSDSE-B-2026 で R² が 0.85 でも、 残差プロット・自由度調整済 R²・Cook 距離による外れ値感度・k-fold CV による外推性能 を併せて評価して、 はじめて信頼できる説明力と判定できる。

🌳 手法選択フロー

「r squared」を含む手法選択は、 データの性質・分析目的・運用制約の 3 軸で決まる。 以下に典型シナリオと対応する手法の組合せを示す。

典型シナリオ別の手法選択

シナリオ重視する観点候補手法
変数が多い重回帰説明変数増加の補正が必要自由度調整済み R² (adjusted R²) を併用
ロジスティック回帰通常 R² が定義されないMcFadden 擬似 R² や Nagelkerke R² を使用
新規データへの汎化性能を見たい訓練時 R² は楽観的交差検証 R² (k=5 や k=10) で評価
予測誤差の絶対量を知りたいR² だけでは尺度感が出ないRMSE / MAE を併記
モデル比較・変数選択R² は単調増加のため不公平AIC / BIC で penalize
外れ値が結果を支配するR² が見かけ上高くなるロバスト回帰 + 残差プロット併用

選んだ後の検証ステップ

  1. R² の絶対値: 0.0-1.0 の値で、 0.7 以上は強い説明力、 0.3 未満は説明力不足の目安だが、 分野 (社会科学 vs 物理) で基準は異なる
  2. 自由度調整済 R²: 説明変数が増えると R² は単調増加するため、 adjusted R² で過剰適合をペナルティ評価
  3. 残差分析: 残差プロットで等分散・独立性・正規性を確認し、 R² 上昇の真因 (本当に説明できているか) を点検
  4. 外れ値感度: Cook 距離やレバレッジで影響点を特定し、 1-2 点で R² が大きく変わらないか確認
  5. 解釈: SSDSE-B-2026 で R² = 0.85 が得られた場合、 残り 15% の未説明分が何 (測定誤差 / 欠落変数) かを分野知識で検討

🔬 補強:R² と分散分解 — TSS = ESS + RSS を SSDSE-B-2026 で実演

R² は「目的変数の分散のうち、 モデルが説明できる割合」と定義されますが、 この分子・分母を実データで分解すると、 R² がなぜ 0〜1 を取るのか、 マイナスになり得るのか、 なぜ訓練データでは「変数を増やすと必ず R² が上がる」のかが明快になります。 SSDSE-B-2026 を使って実演します。

📐 三つの平方和

TSS = ESS + RSS
R² = ESS / TSS = 1 − RSS / TSS

🧮 SSDSE-B-2026 で実値計算

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import pandas as pd, numpy as np
from sklearn.linear_model import LinearRegression

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]
X = df[['A1303', 'A1301']].astype(float)   # 65歳以上人口, 15歳未満人口
y = df['A1101'].astype(float)              # 総人口

model = LinearRegression().fit(X, y)
y_hat = model.predict(X)
y_mean = y.mean()

TSS = ((y - y_mean) ** 2).sum()
ESS = ((y_hat - y_mean) ** 2).sum()
RSS = ((y - y_hat) ** 2).sum()

print(f'TSS = {TSS:.0f}')
print(f'ESS = {ESS:.0f}')
print(f'RSS = {RSS:.0f}')
print(f'TSS - (ESS+RSS) = {TSS-ESS-RSS:.2f}  (理論上 0)')
print(f'R² = ESS/TSS = {ESS/TSS:.4f}')
print(f'R² = 1-RSS/TSS = {1-RSS/TSS:.4f}')
# 出力例: R² ≈ 0.995(総人口は年齢別人口でほぼ完全予測される)

📊 同じデータで「変数を 1 つずつ追加」して R² 上昇を観察

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from sklearn.linear_model import LinearRegression

base_cols = ['A1301']                       # 15歳未満人口
add_cols = ['A1303', 'A4101', 'A4200']      # 65歳以上人口, 出生数, 死亡数

for i in range(len(add_cols) + 1):
    cols = base_cols + add_cols[:i]
    Xi = df[cols].astype(float)
    yi = df['A1101'].astype(float)           # 総人口
    r2 = LinearRegression().fit(Xi, yi).score(Xi, yi)
    print(f'説明変数 {len(cols)} 個: R² = {r2:.6f}')
# R² は単調非減少。 無関係な変数を入れても上がるのが落とし穴。

⚠️ R² がマイナスになる場合 — 何が起きているか

R² < 0 は、 数式上は「モデルが平均値で予測するより悪い」を意味します。 ホールドアウト検証や、 切片無しモデル、 過学習で大きく外れた検証データで起こりがちです。

1
2
3
4
5
6
from sklearn.model_selection import train_test_split

X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.3, shuffle=False)
model_bad = LinearRegression(fit_intercept=False).fit(X_tr, y_tr)
print('test R² (切片なし):', model_bad.score(X_te, y_te))
# 切片無しで強引にフィットすると test R² がマイナスになり得る

📐 R² と相関係数の関係 — 単回帰のみ

単回帰では R² = r²(r は説明変数 X と目的変数 Y の相関係数の絶対値)。 ただし重回帰では R² ≠ Σrⱼ² なので、 「個別変数の貢献」と R² 全体は直接対応しません(→ 標準化β や偏相関で評価)。

指標分子分母解釈
ESSTSS説明割合
MSERSSn平均誤差²
RMSE√RSS√n単位付き誤差
調整 R²RSS/(n-p-1)TSS/(n-1)自由度補正

💡 使い分け:モデル比較なら調整 R² や AIC、 報告書は R² 単独で OK、 予測誤差を生の単位で見たいなら RMSE、 と目的別に並走させましょう。

🌐 補強2:R² の派生・代替指標 — McFadden / Nagelkerke / Cox-Snell / 調整 R²

古典 R² は OLS 線形回帰のための指標ですが、 ロジスティック回帰やポアソン回帰など 非線形・非ガウス モデルでも「説明力」を測りたい場面は多い。 そこで考案されたのが「擬似 R²」(pseudo R²)と呼ばれる代替指標群です。 主要 4 つを比較整理します。

指標 数式 範囲 代表的用途
古典 R² 1 − RSS/TSS (−∞, 1] 線形回帰
McFadden R² 1 − ln L_M / ln L_0 [0, 1) ロジスティック回帰
Cox-Snell R² 1 − (L_0/L_M)^(2/n) [0, <1) 一般化線形モデル全般
Nagelkerke R² Cox-Snell / max(Cox-Snell) [0, 1] Cox-Snell の正規化版
調整 R² 1 − (1−R²)(n−1)/(n−p−1) (−∞, 1] 変数数を罰する版

🔬 「対数尤度」に基づく擬似 R² の発想

非ガウスモデルでは「残差平方和」が定義しにくいため、 代わりに 対数尤度 を使います。 完全モデル(M)と帰無モデル(切片のみ、0)の対数尤度を比べ、 「モデルが帰無モデルからどれだけ改善したか」を尤度の比で測るのが擬似 R² の核心です。

🐍 SSDSE-B-2026 で擬似 R² を計算

 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
import pandas as pd
import statsmodels.api as sm
import numpy as np

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]
df = df.dropna(subset=['A1101', 'A1303'])

# 二値ターゲット:「高齢化率(A1303/A1101)が 30% 以上か」
df['high_aging'] = ((df['A1303'] / df['A1101']) > 0.30).astype(int)

X = sm.add_constant(df[['A1101']].astype(float))
y = df['high_aging']

logit = sm.Logit(y, X).fit(disp=0)
ll_M = logit.llf
ll_0 = logit.llnull
n = len(y)

mcfadden = 1 - ll_M / ll_0
cox_snell = 1 - np.exp(2 * (ll_0 - ll_M) / n)
max_cox = 1 - np.exp(2 * ll_0 / n)
nagelkerke = cox_snell / max_cox

print(f'McFadden R²  = {mcfadden:.4f}')
print(f'Cox-Snell R² = {cox_snell:.4f}')
print(f'Nagelkerke R² = {nagelkerke:.4f}')
# 値の絶対比較は要注意:McFadden 0.2-0.4 で「良いフィット」とされる

⚠️ 擬似 R² の「絶対値での比較」は危険

古典 R² が 0.5 と擬似 R² が 0.5 では意味がまったく違います。 McFadden の 0.2 ≈ 古典 R² の 0.5 と言われることもあり、 「擬似」と付くだけあって直接比較できません。 報告時は必ず「どの擬似 R² か」を明記しましょう。

📐 R² の信頼区間 — ブートストラップで推定

古典 R² の標本分布は複雑なので、 信頼区間はブートストラップで求めるのが実務的です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
from sklearn.linear_model import LinearRegression
import numpy as np

df2 = df.dropna(subset=['A1101', 'A1303']).copy()
X2 = df2[['A1303']].astype(float).values
y2 = df2['A1101'].astype(float).values

r2_boot = []
rng = np.random.default_rng(42)
for _ in range(1000):
    idx = rng.integers(0, len(y2), len(y2))
    r2 = LinearRegression().fit(X2[idx], y2[idx]).score(X2[idx], y2[idx])
    r2_boot.append(r2)

print(f'R² 95% CI: [{np.percentile(r2_boot, 2.5):.4f}, {np.percentile(r2_boot, 97.5):.4f}]')
# 都道府県データのような小標本では区間が広くなることに注意

💡 論文掲載時のチェック:(1) R² 単独で結論しない、 (2) 調整 R² も併記、 (3) 非線形モデルなら擬似 R² と尤度比検定、 (4) 小標本ならブートストラップ CI、 を意識すると説得力が増します。

🔬 補強3:R² の挙動をシミュレーション実験で理解する

R² が「サンプルサイズ」「ノイズの大きさ」「変数の数」でどう動くかを、 SSDSE-B-2026 を素材にした擬似実験で確認します。 教科書の数式だけでは見えない「現場感覚」を養うのが目的です。

🧪 実験 1:ノイズを増やすと R² はどう減るか

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import pandas as pd, numpy as np
from sklearn.linear_model import LinearRegression

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]
df = df.dropna(subset=['A1101', 'A1303'])

X = df[['A1303']].astype(float).values
y_clean = df['A1101'].astype(float).values

print('ノイズ標準偏差倍率 → R²')
y_std = y_clean.std()
rng = np.random.default_rng(0)
for k in [0, 0.1, 0.3, 0.5, 1.0, 2.0, 5.0]:
    noise = rng.normal(0, k * y_std, size=len(y_clean))
    y_noisy = y_clean + noise
    r2 = LinearRegression().fit(X, y_noisy).score(X, y_noisy)
    print(f'  {k:.1f}σ : R² = {r2:.4f}')
# ノイズ k=1σ で R² が半分、 k=5σ でほぼ 0 に

🧪 実験 2:「無関係な変数」を増やすと訓練 R² と汎化 R² はどう乖離するか

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import pandas as pd, numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import KFold

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]
df = df.dropna(subset=['A1101', 'A1303'])

X = df[['A1303']].astype(float).copy().reset_index(drop=True)
y = df['A1101'].astype(float).reset_index(drop=True)

rng = np.random.default_rng(42)
print('追加無関係変数の数 → 訓練R² / CV R²')
for n_extra in [0, 5, 10, 20, 30, 40]:
    Xx = X.copy()
    for j in range(n_extra):
        Xx[f'noise_{j}'] = rng.normal(0, 1, size=len(Xx))
    train_r2 = LinearRegression().fit(Xx, y).score(Xx, y)
    cv_r2s = []
    for tr, te in KFold(n_splits=5, shuffle=True, random_state=1).split(Xx):
        m = LinearRegression().fit(Xx.iloc[tr], y.iloc[tr])
        cv_r2s.append(m.score(Xx.iloc[te], y.iloc[te]))
    print(f'  +{n_extra} 個: 訓練 R² = {train_r2:.4f}, CV R² = {np.mean(cv_r2s):.4f}')
# 訓練 R² は単調増加、 CV R² は途中から低下 = 過学習の証拠

🧪 実験 3:標本サイズが小さいと R² は過大評価される

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import pandas as pd, numpy as np
from sklearn.linear_model import LinearRegression

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]
df = df.dropna(subset=['A1101', 'A1303'])

print('標本サイズ → R² のブートストラップ平均と分散')
rng = np.random.default_rng(0)
for n in [5, 10, 20, 47]:
    r2s = []
    for _ in range(500):
        idx = rng.choice(len(df), n, replace=True)
        m = LinearRegression().fit(df[['A1303']].astype(float).iloc[idx],
                                    df['A1101'].astype(float).iloc[idx])
        r2s.append(m.score(df[['A1303']].astype(float).iloc[idx],
                           df['A1101'].astype(float).iloc[idx]))
    print(f'  n={n:3d}: mean R² = {np.mean(r2s):.4f}, std = {np.std(r2s):.4f}')
# 小標本では R² が高めに出るが分散も大きい

📊 実験結果のまとめ表

実験観察された挙動実務的含意
1. ノイズ線形に R² 低下SNR (信号雑音比) の事前推定が重要
2. 過剰変数訓練 R² と CV R² の乖離必ず CV で検証する
3. 小標本分散と過大評価調整 R² または CV 必須

🌐 R² と関連指標の使い分け早見表

場面 推奨指標 補助指標
線形回帰の説明力RMSE
変数選択調整 R²AIC, BIC
予測性能評価CV R²RMSE, MAE
ロジスティック回帰McFadden R²AUC, 対数尤度
時系列調整 R²残差自己相関

💡 シミュレーション思考の効用:教科書を読むだけでなく、 自分でデータを動かしてみることで、 R² が「単なる数値」でなく「データ構造の鏡」であることが体感できます。 実験コードを写経して、 値を変えてみることをおすすめします。

📚 補強4:R² を扱う実務 TIPS と参考文献

R² は誰でも知っている指標ですが、 「正しく報告する」ためのチェックリストは意外と整理されていません。 ここでは論文掲載・実務報告で役立つ実践 TIPS を整理します。

📋 R² 報告チェックリスト

項目 必須? 理由
R² の値(小数点以下 2-4 桁)必須主指標
調整 R²推奨変数数バイアスの補正
標本サイズ n必須小標本では過大評価
説明変数の数 p必須調整 R² と関連
CV R²(テスト R²)推奨汎化性能
非線形なら擬似 R² 種類必須McFadden / Cox-Snell の区別
RMSE / MAE 併記推奨単位付きの実用解釈

🐍 報告に使える「R² ダッシュボード」関数

 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
import pandas as pd, numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import cross_val_score
from sklearn.metrics import mean_squared_error, mean_absolute_error

def r2_dashboard(X, y, model=None):
    # R² 報告に必要な指標を一覧で返す
    if model is None:
        model = LinearRegression()
    n, p = X.shape
    model.fit(X, y)
    y_hat = model.predict(X)
    r2 = model.score(X, y)
    adj_r2 = 1 - (1 - r2) * (n - 1) / (n - p - 1)
    cv_r2 = cross_val_score(model, X, y, cv=5, scoring='r2').mean()
    rmse = np.sqrt(mean_squared_error(y, y_hat))
    mae = mean_absolute_error(y, y_hat)
    return {
        'n':       n,
        'p':       p,
        'R²':      round(r2, 4),
        '調整 R²': round(adj_r2, 4),
        'CV R²':   round(cv_r2, 4),
        'RMSE':    round(rmse, 2),
        'MAE':     round(mae, 2),
    }

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]
df = df.dropna(subset=['A1101', 'A1303', 'A1301'])
X = df[['A1303', 'A1301']].astype(float)
y = df['A1101'].astype(float)
dashboard = r2_dashboard(X, y)
for k, v in dashboard.items():
    print(f'  {k}: {v}')

📐 R² の業界別「目安」

分野良い R² の目安備考
物理学・工学0.95 以上法則性が強い
疫学・公衆衛生0.3 でも有用個人差が大きい
マクロ経済学0.7 ぐらいで標準時系列の傾向効果
マーケティング0.2-0.5消費者行動のノイズ大
心理学0.1-0.4個人差が大きい
機械学習(画像)用途次第非線形性が強い

「R² が低いから悪いモデル」と短絡しないこと。 分野によっては R²=0.3 でも価値ある発見になり得ます。 大切なのは「同分野のベースラインと比べてどうか」です。

📚 主要参考文献

💡 結論:R² は「便利だが万能ではない」指標。 報告するときは必ず「どんな R² か」「何と比べたか」を明示し、 他の指標と併走させることで、 説得力のある分析になります。

🌐 補強5:R² と ANOVA — F 統計量と分散分析表で読み解く

R² は ANOVA(分散分析)と直結します。 OLS の標準的な「F 検定」は実は「R² の有意性検定」と数学的に等価。 SSDSE-B-2026 で並べて確認します。

📐 R² と F の関係

F = [R² / p] / [(1 − R²) / (n − p − 1)]

つまり R² が大きいほど F も大きく、 帰無仮説「すべての係数 = 0」が棄却されやすくなります。

🐍 SSDSE-B-2026 で F と R² を比較

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
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['SSDSE-B-2026'] == 2023]
df = df.dropna(subset=["A1101", "A1303", "A1301"])

model = smf.ols("A1101 ~ A1303 + A1301", data=df).fit()
print(model.summary())
# R² と F 統計量が表示される。 両者は同一情報の別表現。
anova = sm.stats.anova_lm(model)
print(anova)
# 分散分析表:説明変数ごとの SS(平方和)と F 値

📊 分散分析表の読み方

意味関係する量
df自由度変数数 / 標本サイズ
SS平方和ESS / RSS の構成要素
MS平均平方SS / df
F統計量MS_M / MS_Res
p確率値F 分布から計算

💡 R² = ANOVA の使い分け

💡 使い分けのコツ:R² は「全体の説明力」、 ANOVA は「変数ごとの相対寄与」、 t 検定は「個別の有意性」。 3 つを併走させると、 モデルの全体像が立体的に見えてきます。

📚 補強6:R² FAQ — 7 つの本音質問

Q1. R² が 0.95 なのに予測が外れます。 なぜ?

訓練データの R² は内側のフィット。 ホールドアウトテストデータの R² を確認してください。 過学習の可能性大。

Q2. R² がマイナスになりました。 どうすれば?

テストデータでは普通に起こります。 切片なしモデル、 訓練と分布が違うテストデータ、 過学習などが原因。

Q3. R² 0.3 はモデルが悪い?

分野次第。 心理学・社会科学なら良好、 物理学なら不十分。 同分野の典型 R² と比較しましょう。

Q4. R² と相関係数 r の二乗は同じ?

単回帰では r² = R²。 重回帰では別物。 重回帰で「個別変数の貢献率」を測りたいなら偏 R² または標準化β。

Q5. 説明変数を増やすと必ず R² が上がるのですか?

はい、 訓練データの R² は単調非減少。 だから「変数を増やすたびに R² が上がった」だけでは進歩の証拠になりません。 調整 R² や CV R² を使いましょう。

Q6. ロジスティック回帰で R² を計算したい

McFadden、 Cox-Snell、 Nagelkerke のいずれか。 「擬似 R²」と総称されますが、 古典 R² と数値を直接比較できない点に注意。

Q7. R² が高いから因果関係がある?

違います。 R² は「説明力」であって「因果」ではない。 高い R² + 強い理論的根拠 + 内生性対策があってはじめて因果に近づきます。

🔬 解説を深める — R² は「関係の強さ」そのものではない

このページは既に「分散の説明割合」「変数を足すと必ず上がる」「因果ではない」「テストで負になる」を厚く扱ってきました。 ここではそれらと重ならない独自の角度として、 「R² は残差 SD が元の SD の何倍かを言い換えた量」という RMSE との橋渡しと、 「同じ関係でも X の広がりを狭めると R² は崩れる(範囲制限)」という見落とされがちな落とし穴を、 SSDSE-B-2026 の 2023 年・47 都道府県の実測値で掘り下げます。 題材は x=総人口(A1101)で y=大学学生数(E6302)を単回帰したケースです。

👁️ 直感 ── R²=0.79 は「残差の散らばりが元の46%まで縮んだ」という意味

R² は $1-SS_\text{res}/SS_\text{tot}$ ですが、 これを標準偏差で読み替えると急に体感しやすくなります。 残差の標準偏差 ÷ y の標準偏差 = √(1−R²) という関係が常に成り立つからです(= $\text{RMSE}/\sigma_y=\sqrt{1-R^2}$)。 上の単回帰の実測値がこれです。

量(2023・47 都道府県)
R²(=相関 r=0.889 の2乗)0.789
y(大学学生数)の標準偏差 σ_y105,940 人
モデルの残差 SD(=RMSE)48,605 人
RMSE ÷ σ_y(=√(1−R²))0.459

$\sqrt{1-0.789}=\sqrt{0.211}=0.459$ で、 実測の RMSE÷σ_y(48,605÷105,940=0.459)とぴったり一致します。 つまり 「R²=0.79 の当てはまり」とは「予測の誤差の散らばりが、 何のモデルも無いとき(平均だけで予測)の 46% まで縮んだ」ということ。 R² を「50%の分散を説明」と割合で聞くと立派に思えても、 誤差の物差し(SD)では √ を取るので、 R²=0.5 でも残差 SD はまだ元の 71%(√0.5)しか縮みません。 R² の見かけほど誤差は減っていない、 という感覚は RMSE と併読すると崩れずに済みます。

🚧 落とし穴(重要)── X の範囲を狭めるだけで R² は半分以下に崩れる(範囲制限)

最大の誤解は 「R² は 2 変数の関係の強さそのものを表す固有の数値だ」という思い込みです。 実は R² は 手元のサンプルにおける X の広がり(分散)に強く依存します。 同じ「総人口→大学学生数」の関係でも、 人口 100 万〜300 万人の県だけに絞ると、 傾き(1 人あたりの学生数)はほぼ変わらないのに R² だけが崩れます。

サンプル n 回帰の傾き X(人口)のSD
全 47 都道府県470.03402,767,6300.789
人口 100〜300 万人に限定270.0289516,2270.331

傾きは 0.0340→0.0289 とほぼ横ばい(関係の“強さ”=1人あたり学生数は変わっていない)なのに、 R² は 0.789 から 0.331 へ半分以下に崩落しました。 犯人は X の SD が 276.8 万→51.6 万へ大幅に縮んだこと。 R²=説明された分散÷全分散なので、 X の広がりが小さいと分子(傾き²×Xの分散)が縮み、 同じ残差ばらつきに対して R² が下がるのです。 これが計量経済学で言う 範囲制限(restriction of range)。 「うちのデータでは R² が低いから関係が弱い」と結論する前に、 そもそも X を十分に広い範囲でサンプリングできているかを疑ってください。 逆に、 東京都のような巨大な外れ値を1点含めるだけで X 分散が跳ね上がり R² が水増しされることもあり、 R² は「関係の強さ」ではなく「このサンプルでどれだけ Y のばらつきを説明できたか」に過ぎません

🚀 発展

🔗 関連ページ