論文一覧に戻る 📚 用語解説(ジャストインタイム型データサイエンス教育)
決定係数 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² は、 回帰モデルが目的変数のばらつきを何%説明できたかを表す指標:

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

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

🎯 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² の落とし穴

🐍 Python での R²

🎯 解説: SSDSE-B-2026 の総人口(A1101)を説明変数、 県内総生産(C120120)を目的変数として単回帰を実行し、 決定係数 R² で当てはまりを評価する。 R² は「目的変数の分散のうちモデルが説明できる割合」を表し、 0〜1 の範囲で 1 に近いほど良い当てはまり。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
from sklearn.metrics import r2_score
import statsmodels.api as sm

# sklearn
r2 = r2_score(y, y_pred)

# statsmodels
X = sm.add_constant(x)
model = sm.OLS(y, X).fit()
print(f'R²: {model.rsquared:.3f}')
print(f'調整済みR²: {model.rsquared_adj:.3f}')

# 手で計算
ss_res = ((y - y_pred)**2).sum()
ss_tot = ((y - y.mean())**2).sum()
r2 = 1 - ss_res/ss_tot
📥 入力例: data/raw/SSDSE-B-2026.csv 47 都道府県 × 説明変数 1(A1101), 目的変数(C120120) 東京都 14,047,594 → 県内総生産 約 115 兆円 鳥取県 540,000 → 県内総生産 約 1.9 兆円
📤 実行例: R² = 0.991 傾き b = 7.85 切片 a = -1.34e5 → 人口が県内総生産の 99.1% を説明
💬 読み方: R² が 0.99 と極端に高い理由は、 県内総生産が「人口の規模」にほぼ比例するため。 ただし R² が高い=因果ではなく、 産業構造や生産性などの交絡要因に注意。 社会科学では R² > 0.3 でも実用的と見なされる。

🚧 落とし穴と注意点

🔬 「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 プロセスより)

主要分野のマッピング

分野 主要技術 代表ツール
記述統計要約量、 可視化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²

🧮 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² は必ず増加(または維持)するため、 過学習に注意。
1
2
3
4
5
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(総人口), A4101(出生数), A6101(死亡数) 目的変数: C120120(県内総生産)
📤 実行例: R² (単回帰) = 0.991 R² (重回帰) = 0.994 説明変数追加で +0.3 ポイント上昇
💬 読み方: 重回帰では R² が単回帰より必ず上がる。 しかし「真に説明力が増したか」は調整済み R² で確認する必要がある。 同じ R² なら説明変数が少ないモデルが望ましい(オッカムの剃刀)。

注意: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: C120120(県内総生産)
📤 実行例: model.score(X, y) = 0.9912 r2_score(y, y_pred) = 0.9912 相関係数² = 0.9912 (単回帰時のみ一致)
💬 読み方: 単回帰では R² = r² が成立。 重回帰では成立しない(R² は重相関係数の 2 乗に対応)。 sklearn の score() メソッドは常に R² を返す(予測精度の標準指標)。

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

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

🎯 解説: SSDSE-B-2026 の総人口(A1101)を説明変数、 県内総生産(C120120)を目的変数として単回帰を実行し、 決定係数 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), 目的変数(C120120) 東京都 14,047,594 → 県内総生産 約 115 兆円 鳥取県 540,000 → 県内総生産 約 1.9 兆円
📤 実行例: R² = 0.991 傾き b = 7.85 切片 a = -1.34e5 → 人口が県内総生産の 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(総人口), A4101(出生数), A6101(死亡数) 目的変数: C120120(県内総生産)
📤 実行例: R² (単回帰) = 0.991 R² (重回帰) = 0.994 説明変数追加で +0.3 ポイント上昇
💬 読み方: 重回帰では R² が単回帰より必ず上がる。 しかし「真に説明力が増したか」は調整済み R² で確認する必要がある。 同じ R² なら説明変数が少ないモデルが望ましい(オッカムの剃刀)。

⑤ McFadden 疑似 R²(GLM 用)

🎯 解説: scikit-learn の LinearRegression で計算した R² と、 sklearn.metrics の r2_score 関数で計算した R² が一致することを確認する。 検証用にもう一段、 「相関係数の 2 乗 r²」とも一致するかを単回帰で確認する。
1
2
3
4
5
# statsmodels GLM
res_full = sm.GLM(y, X, family=sm.families.Binomial()).fit()
res_null = sm.GLM(y, np.ones((len(y), 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: C120120(県内総生産)
📤 実行例: model.score(X, y) = 0.9912 r2_score(y, y_pred) = 0.9912 相関係数² = 0.9912 (単回帰時のみ一致)
💬 読み方: 単回帰では R² = r² が成立。 重回帰では成立しない(R² は重相関係数の 2 乗に対応)。 sklearn の score() メソッドは常に R² を返す(予測精度の標準指標)。

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

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

📍 体系階層のパス

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

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

中心の概念から放射状に、 前提・兄弟・発展形・応用先などの関係性を矢印で結びます。 横の繋がりを見るのに最適。 ノードをドラッグ、 ホイールでズーム、 クリックで遷移

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

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

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

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

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

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

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

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

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

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

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

🎨 直感で掴む — 決定係数

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

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

📐 数式または定義

決定係数の中心的な数式は次のとおりです( 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 で再現します。 まず一行で読み込めるよう、 引数を直書きしたシンプル版を示します:

# 最小コード(直書き)
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 が手元にない場合は、 統計データ活用コンペティション公式ページからダウンロードしてください。

⚠️ 補足の落とし穴

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

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

🔬 補強: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 で実値計算

import pandas as pd, numpy as np
from sklearn.linear_model import LinearRegression

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='utf-8', skiprows=1)
X = df[['一般世帯数', '15歳未満人口']].dropna()
y = df.loc[X.index, '人口(総数)']

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.999(人口は世帯数でほぼ完全予測される)

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

from sklearn.linear_model import LinearRegression

base_cols = ['一般世帯数']
add_cols = ['15歳未満人口', '65歳以上人口', '出生数', '死亡数']

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

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

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

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² を計算

import pandas as pd
import statsmodels.api as sm
import numpy as np

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='utf-8', skiprows=1)
df = df.dropna(subset=['人口(総数)', '一般世帯数', '65歳以上人口'])

# 二値ターゲット:「高齢化率が中央値以上か」
df['high_aging'] = ((df['65歳以上人口'] / df['人口(総数)']) > 0.30).astype(int)

X = sm.add_constant(df[['一般世帯数']])
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² の標本分布は複雑なので、 信頼区間はブートストラップで求めるのが実務的です。

from sklearn.linear_model import LinearRegression
import numpy as np

df2 = df.dropna(subset=['人口(総数)', '一般世帯数']).copy()
X2 = df2[['一般世帯数']].values
y2 = df2['人口(総数)'].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² はどう減るか

import pandas as pd, numpy as np
from sklearn.linear_model import LinearRegression

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='utf-8', skiprows=1)
df = df.dropna(subset=['人口(総数)', '一般世帯数'])

X = df[['一般世帯数']].values
y_clean = df['人口(総数)'].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² はどう乖離するか

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='utf-8', skiprows=1)
df = df.dropna(subset=['人口(総数)', '一般世帯数'])

X = df[['一般世帯数']].copy().reset_index(drop=True)
y = df['人口(総数)'].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² は過大評価される

import pandas as pd, numpy as np
from sklearn.linear_model import LinearRegression

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='utf-8', skiprows=1)
df = df.dropna(subset=['人口(総数)', '一般世帯数'])

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[['一般世帯数']].iloc[idx],
                                    df['人口(総数)'].iloc[idx])
        r2s.append(m.score(df[['一般世帯数']].iloc[idx],
                           df['人口(総数)'].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² ダッシュボード」関数

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='utf-8', skiprows=1)
df = df.dropna(subset=['人口(総数)', '一般世帯数', '15歳未満人口'])
X = df[['一般世帯数', '15歳未満人口']]
y = df['人口(総数)']
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² を比較

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="utf-8", skiprows=1)
df.columns = [c.replace("(", "_").replace(")", "_") for c in df.columns]
df = df.dropna(subset=["人口_総数_", "一般世帯数", "15歳未満人口"])

model = smf.ols("人口_総数_ ~ 一般世帯数 + Q("15歳未満人口")", 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² + 強い理論的根拠 + 内生性対策があってはじめて因果に近づきます。