🍰 まずはやさしく
データの重複をチェックする道具です。
分析に不要なデータを見つけるために使います。
スマホの利用時間とアプリの数の関係などで考えます。
この用語がどう使われるかを確認しましょう。
論文中に 「VIF(分散拡大係数)」として登場する用語。
VIF(分散拡大係数) とは:多重共線性の強さを定量化。VIF > 10 は深刻、5以上で注意。1なら他変数と独立。
🍰 まずはやさしく
データの重なりを測る物差しです。
計算結果が正しいか確かめるために使います。
テストの点数と勉強時間の関係を調べる時に便利です。
まずは結論から簡単に説明します。
VIF(Variance Inflation Factor, 分散拡大係数)は、 多重共線性の強さを変数ごとに定量化する指標。 重回帰の診断で必ず計算します。
定義:$\text{VIF}_j = 1 / (1 - R_j^2)$
ここで $R_j^2$ は、 「説明変数 $x_j$ を残りの説明変数で回帰したときの $R^2$」。 もし $x_j$ が他変数で完全に予測できるなら $R_j^2 = 1$ → VIF が無限大。 完全に独立なら $R_j^2 = 0$ → VIF = 1。
目安:
使い方:
Python:from statsmodels.stats.outliers_influence import variance_inflation_factor
VIF の閾値は固い決まりではない:10 という値も慣習で、 厳しめに 3 を採用する研究者もいます。 領域ごとの慣行に従いましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import pandas as pd import numpy as np from statsmodels.stats.outliers_influence import variance_inflation_factor from statsmodels.tools.tools import add_constant # SSDSE-B-2026 を読み込み、2023 年度だけ残す df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = df[df['SSDSE-B-2026'] == 2023] # 規模が連動しやすい人口系の説明変数で VIF を診断 cols = ['A1101', 'A1301', 'A1303', 'A4101'] # 総人口/年少人口/高齢人口/出生数 X = add_constant(df[cols].astype(float)) # VIF は切片込みで計算する vif = pd.DataFrame({ 'feature': X.columns, 'VIF': [variance_inflation_factor(X.values, i) for i in range(X.shape[1])], }) print(vif) |
このページの上にある3つの概念マップ(関係マップ、 包含マップ、 ツリーマップ)でこの概念の位置づけが視覚的に分かります。 関連手法を辿って学習を進めましょう。
統計データ活用コンペティションのSSDSE-B-2026データは、 47都道府県の社会経済データ。 この概念を使って以下のような分析ができます:
| 機能 | Python (pandas) | Python (scipy) |
|---|---|---|
| 要約統計 | df.describe() | stats.describe() |
| 平均 | df.mean() | np.mean() |
| 標準偏差 | df.std() | np.std() |
| 相関 | df.corr() | stats.pearsonr() |
| t検定 | — | stats.ttest_ind() |
| 回帰 | — | stats.linregress() |
| 分布フィッティング | — | stats.norm.fit() |
この概念は、 他の多くの統計概念と密接に関連しています。 ジャストインタイム型学習では、 必要に応じて関連用語へジャンプしながら全体像を構築します。
| グループ | 主要概念 |
|---|---|
| 記述統計 | 平均、 中央値、 最頻値、 分散、 標準偏差、 共分散、 相関係数 |
| 可視化 | ヒストグラム、 散布図、 箱ひげ図、 ヒートマップ |
| 推測統計 | 標本平均、 標準誤差、 信頼区間、 p値、 有意水準 |
| 確率分布 | 正規分布、 t分布、 χ²分布、 F分布、 二項分布 |
| 仮説検定 | t検定、 F検定、 χ²検定、 ノンパラ検定 |
| 回帰 | 単回帰、 重回帰、 OLS、 Ridge、 LASSO |
| 分類 | ロジスティック回帰、 決定木、 SVM、 k-NN |
| 教師なし学習 | クラスタリング、 PCA、 因子分析 |
| 時系列 | ARIMA、 VAR、 指数平滑法、 自己相関 |
| 因果推論 | DiD、 IV、 傾向スコア、 交絡変数 |
| 前処理 | 標準化、 正規化、 欠損値処理、 多重共線性対策 |
| 評価 | R²、 残差、 CV、 RMSE、 効果量 |
このページの概念をマスターすることで、 以下のスキルが身につきます:
このコンペの主要データセット(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 | # 必須ライブラリのインストール pip install pandas numpy scipy statsmodels scikit-learn matplotlib seaborn # 標準的なインポート import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns from scipy import stats from sklearn.preprocessing import StandardScaler from sklearn.model_selection import train_test_split from sklearn.metrics import r2_score, mean_squared_error # 日本語表示の設定(matplotlib) plt.rcParams['font.family'] = 'Hiragino Sans' plt.rcParams['axes.unicode_minus'] = False # データ読み込み(SSDSE は cp932、 2 行目の日本語見出しは skiprows=[1] で除外) df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) print(df.shape) print(df.head()) print(df.describe()) |
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 matplotlib.pyplot as plt import seaborn as sns def quick_eda(df, target=None): """探索的データ分析の基本テンプレート""" print(f"Shape: {df.shape}") print(f"\nColumn types:\n{df.dtypes}") print(f"\nMissing values:\n{df.isnull().sum()}") print(f"\nBasic stats:\n{df.describe()}") # 数値列の可視化 numeric_cols = df.select_dtypes(include=[np.number]).columns df[numeric_cols].hist(bins=20, figsize=(15, 10)) plt.tight_layout() plt.show() # 相関ヒートマップ if len(numeric_cols) > 1: plt.figure(figsize=(12, 10)) sns.heatmap(df[numeric_cols].corr(), annot=True, fmt='.2f', cmap='RdBu_r', center=0) plt.show() # ターゲットがあれば散布図行列 if target and target in df.columns: sns.pairplot(df[numeric_cols[:5]], hue=target if df[target].dtype == 'O' else None) plt.show() |
分析結果を報告する際の標準的な構成:
p値だけでなく効果量も併記するのが現代統計の標準。 主要な指標と Cohen の解釈基準:
| 統計量 | 効果量 | 小 | 中 | 大 |
|---|---|---|---|---|
| 2群平均差 | Cohen's d | 0.2 | 0.5 | 0.8 |
| 相関 | r | 0.1 | 0.3 | 0.5 |
| 線形回帰 | R² | 0.02 | 0.13 | 0.26 |
| ANOVA | η² (eta²) | 0.01 | 0.06 | 0.14 |
| χ² | Cramér's V | 0.1 | 0.3 | 0.5 |
| ロジスティック | Odds Ratio | 1.5 | 2.5 | 4.0 |
VIF(分散拡大係数) がデータサイエンスの体系の中でどこに位置するかを、 3つの異なる視点で可視化します。 同じ情報でも見方を変えると気付きが変わります。
🌐 統計・データサイエンス › 前処理 › 変換 › VIF(分散拡大係数)(多重共線性の強さを測る指標)
中心に VIF(分散拡大係数) を置き、 そこから 多重共線性・重回帰・Ridge回帰・LASSO・最小二乗法・単回帰 計 6 個の用語へ、 前提・兄弟・発展形といった関係を矢印で結んでいます。 線がどちら向きかを見れば、 先に読むべき用語とあとから読む用語が分かります。 ノードはドラッグで動かせ、 ホイールでズーム、 クリックでその用語のページへ遷移します。
大きな円が小さな円を包含する Circle Packing 図。 「VIF(分散拡大係数)」は緑色でハイライト。
長方形を入れ子に分割した Treemap 図。 各分野の規模感を面積で比較。 「VIF(分散拡大係数)」は緑色でハイライト。
| マップ | 分かること | こんな時に見る |
|---|---|---|
| 🔗 関係マップ | 手法間の横の関係(前提→発展→応用) | 「次に何を学べばよい?」 学習順序の判断 |
| ⭕ 包含マップ | 分類体系の入れ子構造(上位⊃下位) | 「この手法はどんなジャンルに属する?」 |
| 🌳 ツリーマップ | 分野の規模比較(面積=ボリューム) | 「データサイエンス全体の俯瞰像」 |
💡 ジャストインタイム学習のヒント:3つの視点を行き来することで、 概念を多角的に理解できます。 包含マップやツリーマップはズーム/ドリルダウンで大分類から細部まで探索できます。
多重共線性診断と対処のトピックを網羅。
SSDSE-B-2026 の数値列で重回帰を組み、 各変数の VIF を計算します。 典型的に「総人口」「世帯数」「就業者数」など強相関の人口系変数で VIF>10 が観測されます。
| 変数(典型例) | VIF 目安 | 判定 | 対処 |
|---|---|---|---|
| 総人口 | > 50 | 深刻 | どれか1つに絞る |
| 世帯数 | > 50 | 深刻 | 主成分化 |
| 就業者数 | > 30 | 深刻 | 人口で割って比率化 |
| 平均所得 | 2〜5 | 許容範囲 | そのまま使用可 |
| 持ち家比率 | 1.5〜3 | 問題なし | 独立性が高い |
1 2 3 4 5 6 7 8 9 10 11 12 | import pandas as pd from statsmodels.stats.outliers_influence import variance_inflation_factor from statsmodels.tools.tools import add_constant df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) d = df[df['SSDSE-B-2026'] == 2023] def vif(cols): X = add_constant(d[cols].astype(float)) return {c: float(round(variance_inflation_factor(X.values, X.columns.get_loc(c)), 1)) for c in cols} print('初期4変数 :', vif(['A1101', 'A1301', 'A1303', 'A4101'])) print('A1101除外後:', vif(['A1301', 'A1303', 'A4101'])) |
summary() の Cond.No. は重要な補完情報なので、 必ず VIF と一緒に報告しましょう。variance_inflation_factor は定数項を含むデザイン行列を期待します。 add_constant を忘れると、 各変数の VIF が異常に大きく見える(中心化なしのため)。 「VIF が全部 100 超だった」とパニックになる前に、 まず const 列があるかを確認してください。 この罠は statsmodels の Stack Overflow 質問の常連です。1 2 3 4 5 6 7 8 9 10 11 12 | import pandas as pd from statsmodels.stats.outliers_influence import variance_inflation_factor from statsmodels.tools.tools import add_constant df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = df[df['SSDSE-B-2026'] == 2023] # 2023 年度の 47 都道府県だけ cols = ['A1101', 'A1301', 'A1303', 'A4101'] # 総人口/年少人口/高齢人口/出生数 num = df[cols].astype(float) X = add_constant(num) for i, c in enumerate(X.columns): if c == 'const': continue print(f'{c}: VIF = {variance_inflation_factor(X.values, i):.2f}') |
1 2 3 4 5 6 7 8 | from sklearn.linear_model import LinearRegression X = num.values cols = num.columns for j, c in enumerate(cols): others = [k for k in range(X.shape[1]) if k != j] r2 = LinearRegression().fit(X[:, others], X[:, j]).score(X[:, others], X[:, j]) vif = 1 / (1 - r2) print(f'{c}: R²={r2:.3f}, VIF={vif:.2f}') |
1 2 3 4 5 | import numpy as np R = num.corr().values vifs = np.diag(np.linalg.inv(R)) for c, v in zip(num.columns, vifs): print(f'{c}: VIF={v:.2f}') |
1 2 3 | import statsmodels.api as sm res = sm.OLS(num['A1101'], add_constant(num.drop(columns=['A1101']))).fit() print(res.summary()) # 末尾に Cond. No. が出る |
| 指標 | 問題視する目安 | 特徴 |
|---|---|---|
| VIF | > 10(厳しめ > 5) | 変数ごと、 1対多 |
| 許容度 1/VIF | < 0.1 | VIF の逆数 |
| 条件数 | > 30 | 全変数構造、 多対多 |
| 最小固有値 | < 0.05 | 相関行列の縮退 |
| GVIF(カテゴリ) | ^(1/2df) > 2 | ダミー群全体 |
🍰 まずはやさしく
データの「かぶり」を直感的に捉える方法です。
計算のズレがどれくらい大きいかを知るために使います。
部活の練習量と試合結果の関係で例えてみます。
イメージを掴むための解説を読みましょう。
VIF は「説明変数同士が強く相関している(多重共線性)と、 推定の分散がどれだけ膨らむか」を示す指標。 VIF=1 で無相関、 10 を超えると要注意、 5 を超えると黄信号。 SSDSE-B-2026 で A1101(総人口)・A1301(15 歳未満)・A1303(65 歳以上)を全部入れると、 A1101 ≈ A1301 + A1302 + A1303 のため VIF が爆発する。
VIF (分散拡大係数) は「回帰」カテゴリの中核概念。 初めて触れる読者は、 まずこの「🎨 直感」セクションだけ通読し、 必要になった時点で「📐 数式」「🐍 Python」「⚠️ 落とし穴」へ戻る読み方が定着しやすいです。
🍰 まずはやさしく
重なり具合を数字にするためのルールです。
正確な値を計算するために使います。
買い物で使う金額と個数の関係などで考えます。
具体的な数式を使って詳しく説明します。
直感の次は、 厳密な定義を確認します。 数式は言語の一種で、 一度書き慣れれば「言葉より速く伝えられる」便利な道具。 慣れていない方は、 各記号が何を表すかを下の「🔬 数式を言葉で読み解く」で 1 つずつ確認してください。
上の数式を眺めるだけでは身につかないので、 各記号がどんな役割を担っているかを言葉で押さえます。 「数式を音読する習慣」がつくと、 論文や教科書を読むスピードが体感で 2 倍ほど上がります。
数式だけでは「実感」が湧きにくいので、 実データ data/raw/SSDSE-B-2026.csv(47 都道府県 × 16 年)で 1 度手計算してみると理解が定着します。
SSDSE-B-2026 (2023) で y=L3221、 説明変数 X={A1101, A1301, A1303} を当てると、 VIF は A1101≒201・A1301≒151・A1303≒56 と 3 変数すべてが VIF≫10 になる(A1101 = A1301 + 15〜64 歳人口 + A1303 という会計恒等式が原因)。 高齢化率(A1303/A1101)へ置き換えると、 その比率変数自体の VIF は約 3 まで下がるが、 規模そのものを表す変数(A1301・A1303)を並べたままでは共線性は解消しないため、 最終的には変数を 1〜2 個に絞る判断が要る。
| 都道府県 | A1101 総人口 | A1303 65 歳以上 | L3221 消費支出 |
|---|---|---|---|
| 東京都 | 14,086,000 | 3,205,000 | 341,320 |
| 神奈川県 | 9,229,000 | 2,390,000 | 306,565 |
| 大阪府 | 8,763,000 | 2,424,000 | 271,246 |
| 愛知県 | 7,477,000 | 1,923,000 | 300,221 |
| 埼玉県 | 7,331,000 | 2,012,000 | 344,092 |
| 千葉県 | 6,257,000 | 1,756,000 | 306,943 |
上記は SSDSE-B-2026 (2023) からの抜粋。 手計算で確認した値が、 後述の Python 実装で得る値と一致することを確認すると、 「数式とコードの対応関係」がクリアに見えるようになります。
公的統計(SSDSE-B-2026)を題材に、 最小限の Python コードで VIF (分散拡大係数) を動作させます。 まずはこのまま実行してみてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # VIF (分散拡大係数) を SSDSE-B-2026 で実行する最小コード import pandas as pd df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = df[df['SSDSE-B-2026'] == 2023] # 2023 年のみ抽出 print(df.shape) # (47, 112) print(df[['Prefecture','A1101','A1303','L3221']].head()) from statsmodels.stats.outliers_influence import variance_inflation_factor import pandas as pd import numpy as np X = df[['A1101','A1301','A1303']].astype(float) X = pd.DataFrame(X.values, columns=X.columns) vifs = [variance_inflation_factor(X.values, i) for i in range(X.shape[1])] for c, v in zip(X.columns, vifs): print(f'{c}: VIF={v:.2f}') |
▶ 実行 を押せばこのページの中でそのまま動きます(ライブラリもデータも同梱済みで、 準備は要りません)。 手元の Python に移して動かすときは pip install matplotlib numpy pandas scikit-learn scipy seaborn statsmodels が必要です。 読んでいるデータは data/raw/SSDSE-B-2026.csv。 日本語を含むので encoding='cp932' の指定を落とさないでください。
VIF (分散拡大係数) を使うときに初学者が踏みやすい失敗パターン。 1 度経験してしまえば次から避けられますが、 先に知っておくに越したことはありません。
VIF(分散拡大係数)は、 説明変数同士の相関が回帰係数の推定分散をどれだけ増幅するかを表す。 多重共線性の有無を視覚化する 3 枚を見ておくとイメージが固まる。



合成 3 変数 R² から VIF を計算する。
| 変数 | R² | VIF |
|---|---|---|
| x1 | 0.20 | 1.25 |
| x2 | 0.70 | 3.33 |
| x3 | 0.95 | 20.00 |
1 2 3 4 | import numpy as np r2 = np.array([0.20, 0.70, 0.95]) vif = 1 / (1 - r2) print(f"VIF: {vif}") |
💬 手計算 (Step 1) と Python 出力が完全一致。
VIF は重回帰の係数解釈を保証する診断指標であり、 以下のフローで隣接手法と連結する。
SSDSE-B-2026 では「規模変数同士の高 VIF (=100 超) → 比率化 (1 人あたり指標) で解消」が定石。
「vif」は統計データ分析の文脈で扱う重要概念のひとつ。 本ページでは「vif」を取り巻く中核キーワードを以下にチップで一覧化する。 各キーワードは関連する概念・手法・道具立てを含み、 文献検索や学習計画の起点になる。
これらのキーワードは「vif の理解 → 適用 → 検証」のプロセスを構成する。 各章で詳しく解説する。
VIF (分散拡大要因) を 30 秒で把握する重要ポイント:
本ページでは「VIF (Variance Inflation Factor、 分散拡大要因)」を扱う。 統計データ分析コンペティション (2026) の教材で、 SSDSE-B-2026 (47 都道府県 × 複数年 × 100 超列) の人口系変数群 (総人口/年少人口/高齢人口/出生数) を題材に、 多重共線性の数値化と対処を再現する。
VIF は回帰分析カテゴリの中核診断指標で、 「OLS の係数解釈が妥当か」を保証する。 本ページは「直感 → 数式 (1/(1-R²_j)) → 47 都道府県データで手計算 → statsmodels.variance_inflation_factor で再現 → 対処 (比率化/正則化/PCA)」の 6 視点で構成される。
VIF (Variance Inflation Factor、 分散拡大要因) は「ある説明変数が他の説明変数とどれだけ重複しているか」を測る指標だ。 例えば SSDSE-B-2026 で「総人口」と「高齢人口」を両方説明変数に入れると、 両者は強く相関しているため、 一方の係数を「高齢人口を一定にしたときの総人口の影響」と読むことが難しくなる。 VIF はこの「重複度」を数値化し、 10 を超えれば多重共線性が深刻と判断する慣習がある。
直感的には「説明変数 X_j を、 他の説明変数で重回帰したときの決定係数 R²_j」を使い、 VIF_j = 1 / (1 - R²_j) で求める。 R²_j が 0.9 なら VIF=10、 0.95 なら VIF=20。 つまり「他の変数で X_j の動きの 9 割が説明できるなら、 X_j を独立に解釈するのは無理」というシグナルになる。
具体例として、 SSDSE-B-2026 の「総人口」「年少人口」「高齢人口」「出生数」の 4 列で VIF を計算する。 次節以降で「数式 → 値代入 → 手計算 → statsmodels.variance_inflation_factor」の流れを再現する。
VIF の定義は明確かつ一意である。 各説明変数ごとに次の式で計算する。
$$\text{VIF}_j = \frac{1}{1 - R_j^2}$$
ここで $R_j^2$ は「説明変数 $X_j$ を、 残りすべての説明変数 $X_{-j}$ で回帰したときの決定係数」である。 つまり「$X_j$ を他の変数たちで予測したときに、 どれだけ説明できるか」。 $R_j^2$ が大きいほど多重共線性が強い。
$$\text{VIF}_j = 1 \ (R_j^2=0) \quad \Rightarrow \quad \text{完全独立}$$
$$\text{VIF}_j = 10 \ (R_j^2=0.9) \quad \Rightarrow \quad \text{多重共線性が強い、 要対処}$$
$$\text{VIF}_j = \infty \ (R_j^2=1) \quad \Rightarrow \quad \text{完全多重共線、 推定不可能}$$
実用的な閾値は VIF=10 (R²_j=0.9)、 厳しい基準では VIF=5 (R²_j=0.8) を採用する。
VIF の数式に含まれる記号を、 日本語の意味に翻訳する。
VIF は「係数推定の標準誤差が独立な場合と比べて何倍になるか」も同時に表す。 VIF=10 なら標準誤差は √10 ≒ 3.16 倍に膨らみ、 t 値が小さく出て「有意でない」と誤判断する原因になる。
SSDSE-B-2026 の「総人口 (A1101)」「年少人口 (A1301)」「高齢人口 (A1303)」「出生数 (A4101)」 の 4 変数で VIF を手計算する。 VIF_総人口 を計算するため、 「総人口 = β0 + β1*年少人口 + β2*高齢人口 + β3*出生数」 の補助回帰を行う。
| Step | 操作 | 数値結果 |
|---|---|---|
| 1 | SSDSE-B-2026 から 4 列抽出(2023 年度) | 47 都道府県 × 4 列 |
| 2 | 補助回帰 1: 総人口 ~ 年少+高齢+出生 | R²_1 ≒ 0.997 |
| 3 | VIF_総人口 = 1 / (1 - 0.997) | ≒ 315 |
| 4 | 補助回帰 2: 年少人口 ~ 総人口+高齢+出生 | R²_2 ≒ 0.998 |
| 5 | VIF_年少人口 = 1 / (1 - 0.998) | ≒ 507 |
| 6 | 判定 | いずれも VIF>10、 多重共線性が極めて強い → 1 変数除去 |
4 変数とも県の人口規模と連動するため、 VIF が爆発的に大きくなる。 実務では「総人口だけ残す」「比率に変換する (高齢化率=高齢人口/総人口)」などの対処が必要。
🎯 このコードでやること: SSDSE-B-2026(2023 年度)の 4 つの人口系変数 (総人口・年少人口・高齢人口・出生数) に対し、 statsmodels の variance_inflation_factor で VIF を計算し、 多重共線性を診断する。
📥 入力データ: SSDSE-B-2026 (47 都道府県 × 100 超列) の CSV。 4 列を抽出する。
1 2 3 4 5 6 7 8 9 10 11 12 13 | import pandas as pd import numpy as np from statsmodels.stats.outliers_influence import variance_inflation_factor from statsmodels.tools.tools import add_constant df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = df[df['SSDSE-B-2026'] == 2023] # 2023 年度のみ cols = ['A1101', 'A1301', 'A1303', 'A4101'] # 総人口/年少人口/高齢人口/出生数 X = add_constant(df[cols].astype(float)) # 切片を加えたデザイン行列 vif = [variance_inflation_factor(X.values, i) for i in range(X.shape[1])] for name, v in zip(X.columns, vif): print(f'{name}: VIF={v:.1f}') |
📤 実行結果:
💬 結果の読み方: const 以外の全変数で VIF が 100 を超え、 極めて深刻な多重共線性。 4 変数すべてが「県の人口規模」と連動しているため。 対処として、 (1) 総人口だけ残す、 (2) 比率化 (高齢化率=A1303/A1101) する、 (3) 主成分分析で次元圧縮、 のいずれかを選ぶ。 A1101 の VIF=314.7 は手計算 (Step 3 の VIF≒315) と一致する。
VIF を実務で使う際の典型的な落とし穴を列挙する。
VIF を中心に、 関連する概念・上位カテゴリ・応用領域を放射状に配置した。
VIF は多重共線性診断の中核指標であり、 前後の手法と以下のように接続する。
| 隣接手法 | 関係 | 接続のポイント |
|---|---|---|
| 重回帰分析 | 上位 / 適用先 | VIF は重回帰の係数解釈の妥当性を診断する補助指標 |
| 相関行列 | 並列 / 下位 | 2 変数間相関だけでは検出できない多重共線性を VIF で補完 |
| 条件数 (κ) | 並列 / 代替 | 全体的な共線性指標。 κ>30 で問題、 κ>100 で深刻 |
| 標準化 / 中心化 | 前段 (前処理) | 2 次項・交互作用項の VIF 膨張を中心化で抑制 |
| リッジ回帰 / Lasso | 後段 (対処) | VIF が高いとき正則化で係数を安定化、 Lasso なら変数除去も自動化 |
| 主成分分析 (PCA) | 後段 (次元圧縮) | VIF が極端に高い (>100) 場合、 PCA で直交化して回帰 |
典型フロー: 重回帰実行 → VIF 計算 → 10 超の変数あり → 比率化 or リッジ or 変数除去 → 再フィット。
VIF の値に応じた対処法の選択フロー。 まず VIF を全変数で計算し、 最大値に応じて分岐する。
| VIF 最大値 | 判定 | 推奨対処 |
|---|---|---|
| 1 〜 5 | 問題なし | 通常の OLS をそのまま使用 |
| 5 〜 10 | 注意 | 係数解釈に注意、 標準誤差を確認、 信頼区間を広めに見積もる |
| 10 〜 30 | 問題あり | 変数除去 (高 VIF 変数の一つを削除) または比率化 |
| 30 〜 100 | 深刻 | リッジ回帰、 Lasso、 Elastic Net で正則化 |
| 100 超 | 致命的 | PCA で直交化、 または変数設計を根本見直し (SSDSE-B-2026 の規模変数群がここに該当) |
| ∞ (完全共線) | 推定不可能 | 線形従属な変数を必ず除去 (ダミー変数のトラップ等を確認) |
VIF は多重共線性の代表指標ですが、 説明変数行列 X の 固有値 と 条件数 に翻訳すると、 なぜ大きい VIF が「数値的不安定」を生むのかが透けて見えます。 SSDSE-B-2026 を題材に、 設計行列の構造から VIF へ降りる道筋を辿ります。
標準化された説明変数行列を Z(n×p)とすると、 OLS 推定量の分散は次の形を取ります。
Var(β̂) = σ² (ZᵀZ)⁻¹
(ZᵀZ) を固有値分解すると ZᵀZ = U Λ Uᵀ(Λ は固有値の対角行列)。 VIF が大きくなる方向 = (ZᵀZ)⁻¹ の固有値が小さい方向 = 共線性の「同じ向き」の固有ベクトル。 つまり VIF=10 以上の変数は、 設計行列のある方向で情報がほぼゼロになっていることを意味します。
1 2 3 4 5 6 7 8 9 10 11 12 | import pandas as pd, numpy as np df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = df[df['SSDSE-B-2026'] == 2023] # 2023 年度のみ X = df[['A1101', 'A4101', 'A1301', 'A1303']].astype(float) # 総人口/出生数/年少/高齢 Z = (X - X.mean()) / X.std() # 共分散行列の固有値 eig = np.linalg.eigvalsh(Z.T @ Z / len(Z)) cond_number = np.sqrt(eig.max() / eig.min()) print('固有値:', np.round(eig, 4)) print('条件数:', round(cond_number, 1)) # 実測出力(2023): 固有値 [≈0.0009, 0.003, 0.021, 3.89]、 条件数 ≈ 64.3 # → 条件数 > 30 で深刻、 100 超で危機的 |
| 条件数 | 最小固有値の挙動 | 推定 VIF レンジ | 解釈 |
|---|---|---|---|
| < 10 | 十分大きい | 1–3 | 健全 |
| 10–30 | 小さくなり始める | 3–10 | 要注意 |
| 30–100 | 10⁻³ オーダー | 10–50 | 深刻 |
| > 100 | 10⁻⁵ 以下 | 100+ | 推定が崩壊 |
VIF が大きい変数群を主成分(PC)に縮約すれば、 PC 同士は直交(共線性ゼロ)。 SSDSE-B-2026 の人口変数 4 つを 2 主成分に縮約すれば、 累積寄与率 99% を保ちつつ VIF=1 を実現できます。
1 2 3 4 5 | from sklearn.decomposition import PCA pca = PCA(n_components=2) Z_pc = pca.fit_transform(Z) print('寄与率:', pca.explained_variance_ratio_.round(3)) # 主成分回帰モデルで Var(β̂) は安定化 |
💡 実務メモ:VIF が示すのは「数値的不安定さ」であって「予測性能の悪さ」ではない。 予測だけが目的なら Ridge 回帰のような正則化、 解釈が目的なら主成分や変数削減、 と目的別に対処法を選びましょう。
「VIF > 10 は深刻」「VIF > 5 は注意」という閾値はどこから来たのでしょうか。 実は厳密な統計的根拠というよりは、 経験的な慣習に近いものです。 SSDSE-B-2026 で実際に検証してみると、 閾値だけに頼ることの危険性が見えてきます。
| 閾値 | 出典 | 含意 |
|---|---|---|
| VIF > 10 | Marquardt (1970) | R²ⱼ > 0.9 に対応 |
| VIF > 5 | 慣習的 | R²ⱼ > 0.8 に対応 |
| VIF > 4 | O'Brien (2007) | R²ⱼ > 0.75 に対応 |
| VIF > 1 | 完全独立しか許さない | 実務的に不可能 |
O'Brien (2007) は A Caution Regarding Rules of Thumb for Variance Inflation Factors で、 VIF=10 という閾値が「絶対的な意味を持たない」と警鐘を鳴らしました。 サンプルサイズ・モデルのフィット・他の説明変数の数を考慮した相対的判断が必要です。
同じ VIF=10 でも、 n=30 と n=3000 では信頼区間の幅がまったく違います。 大標本では VIF=20 でも実用的に問題ないケースがあり、 小標本では VIF=5 でも係数推定が破綻するケースがあります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import pandas as pd, numpy as np import statsmodels.api as sm df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = df[df['SSDSE-B-2026'] == 2023] # 2023 年度のみ (47 都道府県) n = len(df) # 信頼区間の幅は std_err × t_critical に比例 # std_err は √VIF に比例 X = sm.add_constant(df[['A4101', 'A1301', 'A1303']].astype(float)) model = sm.OLS(df['A1101'].astype(float), X).fit() ci = model.conf_int() ci_width = ci[1] - ci[0] print(f'n = {n}') for var in ['A4101', 'A1301', 'A1303']: print(f' {var}: 95% CI 幅 = {ci_width[var]:.2f}') # n=47 では幅が広く、 同じ VIF でも n=470 なら 1/√10 ≈ 0.32 倍 |
| 目的 | VIF 許容範囲 | 理由 |
|---|---|---|
| 予測精度 | VIF=∞ でも可 | 予測の MSE は VIF に影響されない |
| 変数選択 | VIF < 10 推奨 | どの変数が効くかの判定が不安定 |
| 仮説検定 | VIF < 5 推奨 | 標準誤差が膨らみ第二種の過誤増 |
| 構造方程式 | VIF < 2.5 | 構造パラメータの一意性 |
多項式項や交互作用項で VIF が爆発する場合、 元の変数を 平均で引いてから(中心化)二乗・乗算すると、 VIF を劇的に下げられます。 これは数学的に等価な変換ですが、 数値計算上は別物として振る舞います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | import pandas as pd from statsmodels.stats.outliers_influence import variance_inflation_factor df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = df[df['SSDSE-B-2026'] == 2023] # 2023 年度のみ x = df['A1101'].astype(float) # 総人口 # 中心化なし:x と x² X1 = pd.DataFrame({'x': x, 'x2': x ** 2}) print('中心化なし:') for i, c in enumerate(X1.columns): print(f' {c}: VIF = {variance_inflation_factor(X1.values, i):.1f}') # 中心化あり x_c = x - x.mean() X2 = pd.DataFrame({'x_c': x_c, 'x_c2': x_c ** 2}) print('中心化あり:') for i, c in enumerate(X2.columns): print(f' {c}: VIF = {variance_inflation_factor(X2.values, i):.1f}') # 実測(2023): 中心化なし VIF≈5.4、 中心化あり VIF≈2.6(総人口は右に歪むため低減は緩やか) |
💡 実務 4 か条:(1) VIF 単独で判断せず、 標本サイズと目的を併せて評価、 (2) 多項式項は必ず中心化、 (3) ダミー変数は基準カテゴリを意識した解釈、 (4) 高 VIF でも予測目的なら Ridge で正則化、 が王道です。
2つの説明変数 X1・X2 の相関 r をスライダー(またはグラフを指でドラッグ)で動かすと、 散布図と VIF がリアルタイムに変わります。 説明変数どうしが似てくるほど VIF がどう跳ね上がるかを体感してください。
2変数モデルでは「X1 を X2 で回帰した $R^2$」は相関の2乗に一致します($R^2 = r^2$)。 したがって $\text{VIF} = 1/(1-R^2) = 1/(1-r^2)$。 計算は正確です。
VIF は文字どおり「係数推定量の分散を何倍に膨らませるか」を表します。 ある説明変数が他の説明変数でほぼ説明できてしまうと($R^2$ が1に近い)、 回帰はその変数固有の効果を「見分けられなく」なり、 係数の推定が不安定になります。 標準誤差は $\sqrt{\text{VIF}}$ に比例して伸びるため、 VIF=10 なら標準誤差は約 $\sqrt{10}\approx3.16$ 倍。 上のスライダーで r を 0.9 → 0.95 に上げると、 VIF が約 5.3 → 約 10.3 へ跳ね上がり、 VIF>10 の警告境界を越える様子が見えます。
説明変数が3つ以上になると $R^2 = r^2$ の単純式は使えず、 各変数を「残りすべて」で回帰した $R^2_j$ が必要です(実データでの計算例はこのページ上部の Python コードを参照)。 高 VIF への対処は、 (1) 片方の変数を落とす、 (2) 比率化(例:高齢化率)、 (3) 主成分に置き換える、 (4) リッジ回帰で正則化、 が定番です。
ここまでの節(固有値・条件数は「VIF の数式的深掘り」節、 閾値の根拠は「閾値『10』の根拠」節、 変数除去の実演は「実値計算例」節)で扱わなかった別の切り口だけを、 SSDSE-B-2026(2023年・47県)の実測値で補います。
よくある誤解は「VIF は 2 変数の相関係数を見ている」というもの。 正しくは、 変数 $x_j$ を残りの説明変数すべてで重回帰したときの決定係数 $R_j^2$ を使い、 $\text{VIF}_j = 1/(1-R_j^2)$ で定義します。 だからペアごとの相関がどれも中程度でも、 3 変数以上が線形に絡めば VIF は跳ね上がります(その「同じ向き」を固有値・条件数で見るのが別節の話)。
許容度(tolerance)= 1/VIF は「その変数の分散のうち、 他変数で説明しきれず残った割合」を表す裏返しの指標です。 tolerance が 0 に近いほど、 その変数は他変数のほぼ線形結合=固有情報がないことを意味します。 SSDSE-B-2026 の人口系 4 変数(A1101 総人口 / A1301 年少人口 / A1303 高齢人口 / A4101 出生数)の実測:
| 変数 | VIF(実測) | tolerance = 1/VIF | 残った固有情報 |
|---|---|---|---|
| A1101 総人口 | 314.74 | 0.0032 | 約 0.3% |
| A1301 年少人口 | 507.28 | 0.0020 | 約 0.2% |
| A1303 高齢人口 | 116.41 | 0.0086 | 約 0.9% |
| A4101 出生数 | 478.47 | 0.0021 | 約 0.2% |
tolerance が軒並み 1% 未満 = どの人口系変数も「他の 3 つでほぼ再現できる」状態です。
47 県を全国 7 地域(北海道東北 / 関東 / 中部 / 近畿 / 中国 / 四国 / 九州)に区分した地域因子(ダミー 6 本, df=6)を、 標準化した A1101・A4101 と同じモデルに入れて GVIF を計算した実測:
| 項 | 指標 | 実測値 | √ に直した比較値 |
|---|---|---|---|
| 地域因子(df=6) | GVIF | 2.79 | GVIF1/(2·6) = 1.089 |
| A1101 総人口 | VIF | 211.43 | √VIF = 14.5 |
| A4101 出生数 | VIF | 202.06 | √VIF = 14.2 |
読み方:地域因子の $\text{GVIF}^{1/(2df)}=1.089$ は $\sqrt{\text{VIF}}\approx1$ 相当でほぼ無害。 一方、 総人口と出生数は互いに強相関のため $\sqrt{\text{VIF}}\approx14$ と深刻です。 因子全体は GVIF、 単一の連続変数は通常 VIF と使い分けるのが要点。 高 VIF への対処は「除去 / 比率化 / 正則化 / 主成分」(各手法は関連ページへ)。
※ 地域区分は都道府県コードから機械的に 7 区分したもの。 GVIF・VIF・tolerance の数値はすべて SSDSE-B-2026(cp932, skiprows=[1], 2023年47県)からの実測です。