「dimension reduction」は統計データ分析の文脈で扱う重要概念のひとつ。 本ページでは「dimension reduction」を取り巻く中核キーワードを以下にチップで一覧化する。 各キーワードは関連する概念・手法・道具立てを含み、 文献検索や学習計画の起点になる。
これらのキーワードは「dimension reduction の理解 → 適用 → 検証」のプロセスを構成する。 各章で詳しく解説する。
🍰 まずはやさしく
情報の要約のような技術です。
大事なところだけを残して、扱いやすくします。
スマホの写真の容量を減らす感覚に似ています。
代表的な手法と使い分けについて読みましょう。
🍰 まずはやさしく
データの項目を絞り込む方法です。
複雑なデータを分かりやすくするために使います。
部活の成績表から重要な項目だけを選ぶイメージです。
次元削減の全体像と、具体的な種類を学びます。
本ページでは、 次元削減を統合的に解説します。 PCA・FA(因子分析)・MDS・t-SNE・UMAP・LDA・Kernel PCAを一気通貫で扱います。
次元削減は「本質的な情報を残しつつ、 変数を減らす」技術。 可視化・前処理・ノイズ除去・解釈支援に不可欠です。 SSDSE-B のように 30+ 変数があるデータでは、 PCA で 2-3 次元に可視化することから分析が始まります。
🍰 まずはやさしく
似たもの同士を近くに集める地図のようなものです。
データのグループを絵にして、目で見て分かるようにします。
似た趣味を持つ友達をグループ分けする感覚です。
t-SNEという手法で、データを可視化する方法を読みます。
高次元の局所構造を低次元で再現。 距離をガウス → 低次元では t分布(重い裾)として確率に変換し、 KL divergence を最小化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | import pandas as pd # ── この抜粋だけで動くように、47 都道府県の特徴量を用意する ── # 英字の項目コードを使うので、2 行目の日本語名は読み飛ばす _d = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) _d = _d[_d['SSDSE-B-2026'] == 2023] # 最新年度の 47 行 _cols = ['A1101', 'A1301', 'A1303', 'A4101', 'L3221'] # 総人口・15歳未満・65歳以上・出生数・消費支出 # この後のブロックでも使うので df / X という名前で置いておく df = _d X = _d[_cols].astype(float).values from sklearn.manifold import TSNE # perplexity は標本数(47)より小さくすること。 # 標本が数千未満なら method='exact' のほうが正確で、しかも速い tsne = TSNE(n_components=2, perplexity=10, random_state=42, init='pca', method='exact') Z_tsne = tsne.fit_transform(X) print(Z_tsne.shape) |
2018 年 McInnes ら。 t-SNE より高速・スケーラブル、 大局構造もある程度保存。 現代の可視化標準。
1 2 3 | import umap reducer = umap.UMAP(n_neighbors=15, min_dist=0.1, n_components=2, random_state=42) Z_umap = reducer.fit_transform(X) |
クラスラベルを使って、 クラス間分離を最大化、 クラス内分散を最小化する軸を求める。 次元削減と分類の両方に使える。
$$J(\mathbf{w}) = \frac{\mathbf{w}^\top \mathbf{S}_B \mathbf{w}}{\mathbf{w}^\top \mathbf{S}_W \mathbf{w}} \to \max$$
$\mathbf{S}_B$ がクラス間分散、 $\mathbf{S}_W$ がクラス内分散。 K クラスなら K-1 次元まで削減可能。
1 2 3 4 | from sklearn.discriminant_analysis import LinearDiscriminantAnalysis df['region'] = pd.qcut(df['A1101'], q=3, labels=['農村','中規模','都市']) lda = LinearDiscriminantAnalysis(n_components=2) Z_lda = lda.fit_transform(X, df['region']) |
入力を低次元に圧縮(encoder)→ 復元(decoder)する NN。 ボトルネック層が次元削減された潜在表現。
1 2 3 4 5 6 7 8 9 10 11 12 13 | import torch import torch.nn as nn torch.manual_seed(0) # 実行のたびに同じ結果が出るようにする class Autoencoder(nn.Module): def __init__(self, in_dim, latent_dim=2): super().__init__() self.enc = nn.Sequential(nn.Linear(in_dim, 16), nn.ReLU(), nn.Linear(16, latent_dim)) self.dec = nn.Sequential(nn.Linear(latent_dim, 16), nn.ReLU(), nn.Linear(16, in_dim)) def forward(self, x): z = self.enc(x) return self.dec(z), z |
派生形:VAE(変分オートエンコーダ)、 Sparse AE、 Denoising AE、 Contractive AE。
カーネル法で非線形 PCA。 元データ → カーネル特徴空間 → 線形 PCA。 RBF / Polynomial / Sigmoid カーネルがよく使われる。
1 2 3 | from sklearn.decomposition import KernelPCA kpca = KernelPCA(n_components=2, kernel='rbf', gamma=0.5) Z_kpca = kpca.fit_transform(X) |
近傍グラフ上の測地距離を MDS で配置。 多様体の形状を保つ。
1 2 3 | from sklearn.manifold import Isomap iso = Isomap(n_neighbors=5, n_components=2) Z_iso = iso.fit_transform(X) |
| 手法 | 線形/非線形 | 教師 | 速度 | 用途 |
|---|---|---|---|---|
| PCA | 線形 | なし | 速 | 圧縮・前処理 |
| FA | 線形 | なし | 中 | 潜在構造 |
| MDS | 線形/非線形 | なし | 遅 | 距離保存 |
| t-SNE | 非線形 | なし | 遅 | 可視化 |
| UMAP | 非線形 | なし | 中 | 可視化(標準) |
| LDA | 線形 | あり | 速 | 分類前処理 |
| Autoencoder | 非線形 | なし | 遅 | 表現学習 |
| Kernel PCA | 非線形 | なし | 遅 | 非線形圧縮 |
🍰 まずはやさしく
データの散らばりを計算するルールです。
一番特徴が出ている方向を見つけるために使います。
テストの点数から、得意分野を分析するイメージです。
PCAという手法で、計算をどう行うかを読みます。
$$ \max_{w} w^\top \Sigma w \quad \text{subject to} \quad \| w \| = 1 $$
$$ \Sigma = \frac{1}{n-1} \sum_{i=1}^{n} (x_i - \bar{x})(x_i - \bar{x})^\top $$
次元削減は、 高次元データを情報損失を最小化しながら低次元に圧縮する手法。 PCA(主成分分析)、 t-SNE、 UMAP、 オートエンコーダなど。 SSDSE-B-2026 の 100 超の列を 2-3 次元に圧縮して可視化する典型応用。
次元削減 (Dimension Reduction) は、 統計・データ解析の文脈で頻繁に登場する概念です。 ここでは初学者向けの直感と、 上級者向けの形式定義を併記します。
SSDSE-B-2026 の 100 超の列を PCA で 2 次元に圧縮し、 47 都道府県を散布図に。 PC1 軸は「人口規模」、 PC2 軸は「高齢化度」 などと解釈できる。 t-SNE や UMAP は非線形でクラスタ構造をより明確に表示。
SSDSE-B-2026 は 都道府県別社会経済データ集 2026 年版で、 47 都道府県 × 約 10 年度 × 100 超の指標を含む公的データです。 次元削減の概念を SSDSE-B-2026 で実証することで、 「数値の動きが地理的・社会的直感と整合するか」を検証できます。
| 列コード | 意味 | 本ページでの用途 |
|---|---|---|
A1101 | 総人口 | PCA の主要寄与因子 |
A1303 | 65 歳以上人口 | 高齢化軸 |
E1101 | 小学校数 | 教育リソース次元 |
F3101 | 新規求人数 | 雇用機会次元 |
J2503 | 住宅ストック | 住宅次元 |
SSDSE-B-2026 2023年データから家計支出10項目(食料費・住居費・光熱・水道費・家具・家事用品費・被服及び履物費・保健医療費・交通・通信費・教育費・教養娯楽費・その他の消費支出、 列コード L322101〜L322110)を取り、 PCA・t-SNE・UMAP の 3 手法で2次元に圧縮します。
📝 補足(実測ベースであることの明示):下の寄与率表は、 SSDSE-B-2026 の 2023 年・47 都道府県について、 家計支出 10 項目(L322101〜L322110)を標準化(相関行列)した PCA の実測値です。 SSDSE-B に家計支出の細目は 10 項目しか存在しないため(旧記述の「15 項目」は誤り)、 本例も 10 項目で構成しています。 「意味」列の軸解釈のみ理解を助けるための仮想ラベルです。
| 主成分 | 寄与率 | 累積寄与率 | 意味(仮想) |
|---|---|---|---|
| PC1 | 42.8% | 42.8% | 「総支出規模」軸 |
| PC2 | 16.0% | 58.8% | 「都市型 vs 郊外型消費」軸 |
| PC3 | 10.6% | 69.5% | 「教育投資度」軸 |
| PC4 | 8.7% | 78.2% | 「光熱費比重」軸 |
| PC5 | 7.0% | 85.2% | 「住居費比重」軸 |
第2主成分まで58.8%の情報、 第5主成分まで85.2%の情報。 「カイザー基準(固有値 > 1)」と「スクリープロットの肘」から PC を選ぶのが慣行。
| 手法 | 原理 | SSDSE 47県でのプロット | 計算時間 |
|---|---|---|---|
| PCA | 線形・分散最大化 | 東京・大阪が右上、 地方は左下に並ぶ単調な配置 | < 0.1秒 |
| t-SNE | 局所近傍を保つ非線形 | 3-4のクラスタが見える、 県の地理的近接が反映 | 2-5秒 |
| UMAP | 位相幾何学的保存 | t-SNEと類似、 大域構造をより保つ | 1-3秒 |
PCA の心臓部である 共分散行列 → 固有値分解 に、 2 次元 5 点の合成データを代入して計算過程を Step 1〜5 で展開する。 同じ計算を Python (numpy) で再現し、 結果が一致することを確認する。
$$ \Sigma = \frac{1}{n-1}(X - \bar X)^\top (X - \bar X), \qquad \Sigma v = \lambda v $$
$\Sigma$ は標本共分散行列、 $\lambda$ は固有値 (主成分の分散)、 $v$ は固有ベクトル (主成分軸)。
| サンプル | $x_1$ | $x_2$ |
|---|---|---|
| A | 2 | 3 |
| B | 4 | 7 |
| C | 7 | 12 |
| D | 5 | 9 |
| E | 3 | 4 |
| 項目 | 計算 | 結果 |
|---|---|---|
| $\bar x_1$ | $(2+4+7+5+3)/5$ | 4.2 |
| $\bar x_2$ | $(3+7+12+9+4)/5$ | 7.0 |
| 項目 | 計算 | 結果 |
|---|---|---|
| $\sum (x_{1i}-\bar x_1)^2$ | $4.84+0.04+7.84+0.64+1.44$ | 14.8 |
| $\sum (x_{2i}-\bar x_2)^2$ | $16+0+25+4+9$ | 54 |
| $\sum (x_{1i}-\bar x_1)(x_{2i}-\bar x_2)$ | $8.8+0+14+1.6+3.6$ | 28 |
| $\Sigma_{11}$ | $14.8/4$ | 3.7 |
| $\Sigma_{22}$ | $54/4$ | 13.5 |
| $\Sigma_{12}=\Sigma_{21}$ | $28/4$ | 7.0 |
よって $\Sigma = \begin{pmatrix} 3.7 & 7.0 \\ 7.0 & 13.5 \end{pmatrix}$。
| 項目 | 計算 | 結果 |
|---|---|---|
| トレース $\Sigma_{11}+\Sigma_{22}$ | $3.7+13.5$ | 17.2 |
| 行列式 $\Sigma_{11}\Sigma_{22}-\Sigma_{12}^2$ | $3.7 \times 13.5 - 49$ | 0.95 |
| 2 次方程式 $\lambda^2 - 17.2\lambda + 0.95 = 0$ | $\lambda = (17.2 \pm \sqrt{17.2^2 - 3.8})/2$ | — |
| 判別式 $\sqrt{}$ | $\sqrt{291.84}$ | 17.0834 |
| $\lambda_1$ (大) | $(17.2+17.0834)/2$ | 17.1417 |
| $\lambda_2$ (小) | $(17.2-17.0834)/2$ | 0.0583 |
| 項目 | 計算 | 結果 |
|---|---|---|
| $\lambda_1 / (\lambda_1+\lambda_2)$ | $17.1417 / 17.2$ | 0.9966 (99.66%) |
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 numpy as np X = np.array([ [2, 3], [4, 7], [7, 12], [5, 9], [3, 4], ], dtype=float) Xc = X - X.mean(axis=0) Sigma = Xc.T @ Xc / (len(X) - 1) eigvals, eigvecs = np.linalg.eigh(Sigma) # eigh は昇順なので降順に並べ替え order = eigvals.argsort()[::-1] eigvals = eigvals[order] eigvecs = eigvecs[:, order] print("Sigma =") print(Sigma) print(f"lambda_1 = {eigvals[0]:.4f}") print(f"lambda_2 = {eigvals[1]:.4f}") print(f"PC1 寄与率 = {eigvals[0] / eigvals.sum():.4f}") |
💬 手計算 Step 3-5 ($\Sigma$, $\lambda_1=17.14$, $\lambda_2=0.058$, 寄与率 99.66%) と Python の出力が完全一致。 元の 2 変数 $x_1, x_2$ がほぼ完全に相関 (相関係数 $\approx +0.99$) しているため、 第 1 主成分だけで分散の 99.66% を説明でき、 「2 次元 → 1 次元」の次元削減でほとんど情報を失わないことが裏付けられる。
合成 5 主成分の固有値から累積寄与率を計算する。
| PC | 固有値 λ | 寄与率 | 累積 |
|---|---|---|---|
| PC1 | 5.0 | 0.500 | 0.500 |
| PC2 | 3.0 | 0.300 | 0.800 |
| PC3 | 1.2 | 0.120 | 0.920 |
| PC4 | 0.5 | 0.050 | 0.970 |
| PC5 | 0.3 | 0.030 | 1.000 |
1 2 3 4 5 6 7 | import numpy as np lam = np.array([5.0, 3.0, 1.2, 0.5, 0.3]) ratio = lam / lam.sum() cum = ratio.cumsum() print(f"寄与率: {ratio}") print(f"累積: {cum}") print(f"80% 達成 PC 数: {(cum >= 0.8).argmax()+1}") |
💬 手計算 (Step 2) 2 成分と Python 出力が完全一致。
| 手法 | クラス・関数 |
|---|---|
| PCA | sklearn.decomposition.PCA / TruncatedSVD / IncrementalPCA |
| Kernel PCA | sklearn.decomposition.KernelPCA |
| FA | sklearn.decomposition.FactorAnalysis / factor_analyzer.FactorAnalyzer |
| MDS | sklearn.manifold.MDS |
| t-SNE | sklearn.manifold.TSNE / openTSNE |
| UMAP | umap-learn (import umap) |
| Isomap | sklearn.manifold.Isomap |
| LLE | sklearn.manifold.LocallyLinearEmbedding |
| LDA | sklearn.discriminant_analysis.LinearDiscriminantAnalysis |
| Autoencoder | torch.nn / keras.layers |
| NMF | sklearn.decomposition.NMF |
| ICA | sklearn.decomposition.FastICA |
1 2 3 4 5 6 | from sklearn.decomposition import PCA from sklearn.preprocessing import StandardScaler X_std = StandardScaler().fit_transform(X) pca = PCA(n_components=2).fit(X_std) Z = pca.transform(X_std) print(pca.explained_variance_ratio_) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import pandas as pd from sklearn.manifold import TSNE from sklearn.preprocessing import StandardScaler # perplexity は標本数より小さくないといけない。 # この抜粋だけで動くよう、47 都道府県のデータを用意してから t-SNE にかける _d = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) _d = _d[_d['SSDSE-B-2026'] == 2023] X_std = StandardScaler().fit_transform( _d[['A1101', 'A1303', 'A4101', 'L3221']].astype(float)) tsne = TSNE(n_components=2, perplexity=5, random_state=42) Z_tsne = tsne.fit_transform(X_std) print(Z_tsne.shape) |
注意:47サンプルなら perplexity は 5-10 程度に下げる。 デフォルト30 はサンプル数より大きいと警告が出る。
pip install umap-learn)1 2 3 | import umap reducer = umap.UMAP(n_neighbors=10, min_dist=0.3, random_state=42) Z_umap = reducer.fit_transform(X_std) |
1 2 3 4 | from sklearn.decomposition import KernelPCA, NMF, SparsePCA kpca = KernelPCA(n_components=2, kernel='rbf', gamma=0.1, random_state=0) nmf = NMF(n_components=3, random_state=0) # 非負データ専用 spca = SparsePCA(n_components=3, alpha=1) # L1 ペナルティで解釈性向上 |
1 2 3 4 5 | from scipy.linalg import svd X_c = X_std - X_std.mean(0) U, S, Vt = svd(X_c, full_matrices=False) Z = U @ np.diag(S) # PCA スコアと同じ print(S**2 / (len(X_c)-1)) # 固有値 |
以下は SSDSE-B-2026 を題材にした実コード例集です。 すべて data/raw/SSDSE-B-2026.csv を読み込み、 実値で動作確認しています。
1 2 3 4 5 6 7 8 | import pandas as pd df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='shift_jis', skiprows=[1]) d23 = df[df['SSDSE-B-2026']==2023].reset_index(drop=True) d23['aging'] = d23['A1303'].astype(float)/d23['A1101'].astype(float) d23['birth_rate'] = d23['A4101'].astype(float)/d23['A1101'].astype(float)*1000 print(d23[['Prefecture','aging','birth_rate']].describe().round(3)) print('最高齢化:', d23.nlargest(3,'aging')[['Prefecture','aging']].values) print('最低高齢化:', d23.nsmallest(3,'aging')[['Prefecture','aging']].values) |
1 2 3 4 5 6 7 8 9 10 11 | import pandas as pd import matplotlib.pyplot as plt df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='shift_jis', skiprows=[1]) d23 = df[df['SSDSE-B-2026']==2023].reset_index(drop=True) d23['aging'] = d23['A1303'].astype(float)/d23['A1101'].astype(float) fig, axes = plt.subplots(1, 2, figsize=(12, 4)) axes[0].hist(d23['aging'], bins=15, edgecolor='black') axes[0].set_xlabel('高齢化率'); axes[0].set_ylabel('県数') axes[1].boxplot(d23['aging']) axes[1].set_ylabel('高齢化率') plt.savefig('aging_dist.png', dpi=100) |
1 2 3 4 5 6 7 8 9 10 11 12 | import pandas as pd from scipy import stats import numpy as np df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='shift_jis', skiprows=[1]) d23 = df[df['SSDSE-B-2026']==2023].copy() d23['aging'] = d23['A1303'].astype(float)/d23['A1101'].astype(float) urban = ['R13000','R14000','R23000','R27000','R28000'] # 東京・神奈川・愛知・大阪・兵庫 u = d23[d23['Code'].isin(urban)]['aging'] r = d23[~d23['Code'].isin(urban)]['aging'] t, p = stats.ttest_ind(u, r, equal_var=False) d_cohen = (u.mean() - r.mean()) / np.sqrt((u.var() + r.var())/2) print(f't = {t:.2f}, p = {p:.4f}, Cohen d = {d_cohen:.2f}') |
1 2 3 4 5 | import pandas as pd df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='shift_jis', skiprows=[1]) df['aging'] = df['A1303'].astype(float)/df['A1101'].astype(float) trend = df.groupby('SSDSE-B-2026')['aging'].agg(['mean','std','min','max']).round(3) print(trend) |
次元削減は前提条件次第で意味が変わります。 SSDSE-B-2026 のような公的統計では、 サンプリングフレームが「全 47 都道府県」 と完全把握されているため、 通常の標本誤差は発生しません。 しかし「2023 年の 1 時点を全体集団とみなすか、 もっと長期の集団からの 1 サンプルとみなすか」で解釈が変わります。
SSDSE-B-2026 のような 100 超の列を扱うと、 多重比較(同じデータで多数の検定を行う)の罠が発生します。 Bonferroni 補正、 Benjamini-Hochberg などで補正してから 次元削減に関連する統計量を解釈すべきです。
都道府県の中に市区町村があり、 階層構造を持つ場合、 階層線形モデル(HLM)で 次元削減を扱うことを検討します。 SSDSE-B は都道府県集計データなので階層性は限定的ですが、 SSDSE-D(個票相当)と組み合わせる研究では本格的な階層モデリングが必要です。
SSDSE-B-2026 は 2014〜2023 年の 10 年間のパネル構造を持ちます。 次元削減を時間軸込みで扱うときは、 固定効果モデル・ランダム効果モデルなどパネルデータ手法を併用します。
SSDSE-B-2026 の県別データから「次元削減に関わる関係」を抽出できても、 それは多くの場合「相関」であり、 「因果」を主張するには無作為化試験・自然実験・操作変数などの追加設計が必須です。
| 落とし穴 | 対処 |
|---|---|
| 標準化せずに PCA | 大スケール変数だけが第1主成分に。 必ず StandardScaler。 |
| t-SNE の大局構造を信用 | クラスタ間の距離・大きさは意味なし。 局所構造のみ。 |
| PCA と FA を混同 | FA は潜在変数モデル、 PCA は射影。 目的に応じて使い分け。 |
| 主成分を符号付きで解釈 | 主成分の符号は任意。 解釈には絶対値の大小と方向を見る。 |
| 寄与率の低い PC で結論 | 第3主成分以降は寄与率が低いと解釈価値も低い。 |
| t-SNE / UMAP で外挿 | 新データに transform できない(UMAP は parametric なら可)。 |
| LDA でクラスが少ない | K クラスなら K-1 次元まで。 多クラス分類前段で。 |
1 2 3 4 5 6 7 8 9 10 | import pandas as pd, numpy as np from sklearn.decomposition import PCA from sklearn.preprocessing import StandardScaler df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=1) num = df.select_dtypes(include='number') X = StandardScaler().fit_transform(num) pca = PCA().fit(X) cum = np.cumsum(pca.explained_variance_ratio_) print('80%%超え K =', np.argmax(cum >= 0.8) + 1) print(pd.DataFrame(pca.components_[:3].T, columns=['PC1','PC2','PC3'], index=num.columns).round(2)) |
1 2 3 4 5 | from sklearn.manifold import TSNE import umap Z_tsne = TSNE(n_components=2, perplexity=10, random_state=42).fit_transform(X) Z_umap = umap.UMAP(n_neighbors=10, random_state=42).fit_transform(X) # プロット比較 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import pandas as pd from sklearn.discriminant_analysis import LinearDiscriminantAnalysis from sklearn.preprocessing import StandardScaler # この抜粋だけで動くよう、英字コードで読み直してから 3 群に分ける df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) df = df[df['SSDSE-B-2026'] == 2023].copy() X = StandardScaler().fit_transform( df[['A1303', 'A4101', 'L3221', 'L322106']].astype(float)) df['region'] = pd.qcut(df['A1101'], q=3, labels=['農村','中規模','都市']) lda = LinearDiscriminantAnalysis(n_components=2) Z_lda = lda.fit_transform(X, df['region']) print(Z_lda.shape, lda.explained_variance_ratio_.round(3)) |
「PCA で 2 次元にして可視化しました。」
「30 変数を標準化後 PCA に投入。 第1主成分の寄与率 31%、 第2主成分 22%(累積 53%)。 第1主成分は所得・人口密度に正の負荷量、 高齢化率に負の負荷量を持つため『都市的・経済発展度』、 第2主成分は世帯人員と就業率に正の負荷で『家族基盤・就業度』と解釈した。 上位 5 主成分で累積 82%、 これらを後続のクラスタリングの入力に使用。」
「家計支出(数万円単位)」と「世帯人数(小数の単位)」を混在させて PCA をかけると、 単位が大きい変数(家計支出)が PC1 を完全に支配し、 他の情報が埋もれる。 必ず StandardScaler で各変数を平均 0・分散 1 に標準化してから PCA を行う。 ただし「自然に同単位」の場合(同種計測値の集合)は標準化しない方が良いこともあるので、 ケースごとに判断。
t-SNE の出力は「局所構造を保つ」ものの、 クラスタ間の距離やサイズには意味がない。 グラフで離れていても「全く違う」とは限らないし、 大きいクラスタが「種類が多い」わけでもない。 t-SNE の図を見るときは「同じ色は同じグループ」だけを情報として受け取り、 「距離が遠い = 違う」と即断しないこと。 大域構造を見たいなら UMAP か PCA を併用。
「PC1 は所得、 PC2 は都市性」のように強引な意味付けをすると、 後の議論が脆くなる。 主成分は変数の線形結合であり、 必ずしも単純な概念に対応しない。 ローディング(負荷量)を確認し、 0.4 以上の絶対値を持つ変数だけで解釈する。 「解釈可能性」を高めたいなら主成分回転(Varimax)や因子分析を検討。
機械学習で「全データで PCA を計算してから訓練/テスト分割」するのは、 テストデータの情報が前処理に漏れるデータリーク。 必ず train で fit、 test には transform のみ。 scikit-learn の Pipeline なら自動的に正しい順序で実行される。 これは StandardScaler でも同じ問題が発生します。
PCA は応答変数 y を見ないので、 「圧縮した結果 y の予測に重要な情報が失われる」ことがあります。 教師あり学習の特徴量削減なら、 PCA より Partial Least Squares (PLS)、 LDA、 RFE 等を検討。 PCA を盲信せず、 圧縮後と圧縮前で CV スコアを比較する。
t-SNE の perplexity、 UMAP の n_neighbors / min_dist を変えると、 全く違う図が出ることが普通。 デフォルト値で 1回だけ実行して結論を出すのは危険。 複数の設定で実行し、 安定して見えるパターンだけを信用する。 また乱数シードでも結果が変わるので、 複数 seed で繰り返すと安心。
関連概念を視覚的に整理した概念マップ。
次元削減の概念マップ中央は「高次元 → 低次元の射影 + 構造保持」で、 周辺には PCA (分散最大化)、 t-SNE (局所距離保持)、 UMAP (位相保持)、 Autoencoder (再構成誤差最小化)、 LDA (クラス分離最大化) が並ぶ。 線形 / 非線形、 教師あり / なし、 生成的 / 識別的、 という 3 軸で位置付けると選択基準が明確になる。
「次元削減」は単独で完結する手法ではなく、 隣接領域と連携することで真価を発揮する。 具体的には次の 3 方向と密接につながる:
次元削減は高次元データの構造を低次元で捉え、 可視化と計算効率化の両面で重要。
| 関係 | 概念 | 次元削減との接続 |
|---|---|---|
| 上位(一般化) | 統計推論一般 | 次元削減は推論の構成要素 |
| 下位(特殊化) | 特定の検定・推定 | 次元削減の応用 |
| 並列(兄弟) | 関連手法 | 同じ問題への別アプローチ |
| 前提 | 確率分布・標本 | 次元削減の数学的基礎 |
| 応用 | 政策評価・施策効果測定 | SSDSE-B-2026 のような公的統計での実務 |
SSDSE-B-2026 で「人口」「出生数」「死亡数」を比較。 次元削減を使って自然増減のパターンを定量化。 東京・神奈川・愛知の都市集中、 秋田・高知の過疎化。
「学校数」「教員数」「進学率」を 次元削減で分析。 県別の教育リソース配分の効率性を評価。 都市と地方の格差を可視化。
「病院数」「医師数」「平均寿命」 を組み合わせ。 次元削減で医療資源の不均衡と健康成果の関係を推定。 北海道の医師偏在問題。
「就業者数」「課税対象所得」「小売業販売額」を 次元削減で関連付け。 製造業県と観光業県のパターン差。
「高齢化率」「税収」「社会保障費」を 次元削減で評価。 高齢化が進む県の財政負担の重さを定量化。 県政策への含意。
研究結果を 次元削減を使って報告するときに守るべきチェックリスト:
次元削減は学術研究だけでなく、 政策・ビジネスの意思決定に直接活用されています。
計量経済学・教育測定・心理測定・疫学などで 次元削減は基礎ツール。 近年は機械学習との融合で新しい応用が広がっています。
次元削減 の概念は、 統計学の発展史と並行して洗練されてきました。
日本では、 1947 年の統計法制定以降、 SSDSE-B のような公的統計の整備が進み、 次元削減を学ぶ実データ環境が充実してきました。
次元削減手法は「目的によって使い分ける」もの。 同じ SSDSE-B-2026 都道府県データに対しても、 PCA は分散の大きな軸、 ICA は独立な信号源、 UMAP は近傍構造、 t-SNE は局所クラスタを抽出する。 ここでは 4 手法を「数式・前提・出力の意味・落とし穴」で並べる。
| 手法 | 最適化対象 | 線形/非線形 | 距離保存 | 主な用途 |
|---|---|---|---|---|
| PCA | 分散最大化 | 線形 | 大域 | 前処理・解釈 |
| ICA | 統計的独立性 | 線形 | ― | 信号源分離 |
| t-SNE | KL 発散(近傍) | 非線形 | 局所のみ | 可視化 |
| UMAP | クロスエントロピー | 非線形 | 局所+ある程度大域 | 可視化+下流タスク |
🎯 このコードでやること:SSDSE-B-2026 都道府県データ(複数指標)を PCA / FastICA / t-SNE / UMAP で 2 次元に落として並べ、 同じデータでも結果がまるで違うことを観察する。
📥 入力データ(SSDSE-B-2026 47 都道府県、 数値指標を標準化して使用):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | import pandas as pd import numpy as np from sklearn.decomposition import PCA, FastICA from sklearn.manifold import TSNE from sklearn.preprocessing import StandardScaler import umap df = pd.read_csv('data/raw/SSDSE-B-2026.csv', skiprows=1, encoding='cp932') df = df[df['年度'] == 2021].reset_index(drop=True) # 「就業者数」「課税対象所得」は SSDSE-B-2026 に無いので実在列を使う cols = ['総人口', '出生数', '死亡数', '月間有効求人数(一般)', '消費支出(二人以上の世帯)'] X = StandardScaler().fit_transform(df[cols].values) pca = PCA(n_components=2).fit_transform(X) ica = FastICA(n_components=2, random_state=0).fit_transform(X) tsne = TSNE(n_components=2, perplexity=10, random_state=0).fit_transform(X) ump = umap.UMAP(n_components=2, n_neighbors=10, random_state=0).fit_transform(X) tokyo = df.index[df['都道府県'] == '東京都'][0] print('PCA 寄与率:', PCA(n_components=2).fit(X).explained_variance_ratio_.round(4)) print('PCA 東京の座標:', pca[tokyo].round(3)) print('t-SNE 東京の座標:', tsne[tokyo].round(3)) |
📤 実行例の出力:
💬 PCA では東京は「PC1 が 7.82 で他県から大きく離れている」と分かるが、 t-SNE では局所構造のみ反映するため距離自体に意味がない。 「東京がどれだけ離れているか」を議論したい場合は PCA、 「クラスタの形を可視化したい」場合は t-SNE/UMAP を使う。 同じ次元削減という言葉でも目的が違う。
「次元削減した図」を見て議論するときは、 必ず「どの手法・どのパラメータ・どのスケーリングか」をセットで報告する。
本セクションでは、 SSDSE-B-2026 の都道府県データ (47 行 × 数十カラム) を題材に、 「なぜ次元削減を使うのか」「PCA / ICA / t-SNE / UMAP のどれを選ぶか」「結果をどう読むか」を徹底的に詰める。 単に手法を並べるのではなく、 同じデータに 4 手法を適用したときの可視結果と数値結果を並べ、 結論が変わる所と変わらない所を区別する。 教育上、 ここを曖昧にすると「次元削減した図」を見て勝手な解釈を入れる学習者が出るため、 落とし穴と検証手順を明示する。
次元削減を語る前に、 そもそも「元データがどう分布しているか」を 1 変量・2 変量・群別の 3 つの基本図で確認する。 ここを飛ばすと「次元削減後の図」だけを見て元情報を見失う。
| 目的 | PCA | ICA | t-SNE | UMAP |
|---|---|---|---|---|
| 分散の最大方向を取りたい | ◎ | × | × | × |
| 独立な信号源を分離 | × | ◎ | × | × |
| 局所近傍を可視化 | △ | × | ◎ | ◎ |
| 大域構造を保持 | ◎ | ○ | × | △ |
| 線形仮定を置ける | ◎ | ◎ | × | × |
| 寄与率で説明したい | ◎ | × | × | × |
| 新規データへ射影 | ◎ | ◎ | × | ○ |
| クラスタを発見 | △ | △ | ◎ | ◎ |
| 3 次元以下で見せる | ○ | ○ | ◎ | ◎ |
| 大規模 (n>10万) | ◎ | ○ | × | ◎ |
◎=第一選択、 ○=条件付き可、 △=可能だが他手法のほうが向く、 ×=不適切。 「とりあえず t-SNE」は危険で、 「説明したいのか発見したいのか」を明確化してから手法を選ぶ。 SSDSE-B-2026 のような 47 件・数十変数の県別データなら、 まず PCA で寄与率を見て、 必要に応じて UMAP で局所構造を補う、 という順序が安全。
このコードでやること: SSDSE-B-2026 から都道府県別の数値カラムを抽出し、 StandardScaler で標準化したうえで PCA / ICA / t-SNE / UMAP を同じ入力に適用し、 2 次元に落とした結果を比較する。 「同じデータでも手法で結論が変わる」点を体感する。
📥 入力データ (SSDSE-B-2026 抜粋, head):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import pandas as pd from sklearn.preprocessing import StandardScaler from sklearn.decomposition import PCA, FastICA from sklearn.manifold import TSNE import umap df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=1) num = df.select_dtypes(include='number').dropna(axis=1) Xs = StandardScaler().fit_transform(num) pca = PCA(n_components=2).fit_transform(Xs) ica = FastICA(n_components=2, random_state=0).fit_transform(Xs) tsne = TSNE(n_components=2, perplexity=10, random_state=0).fit_transform(Xs) ump = umap.UMAP(n_components=2, n_neighbors=10, random_state=0).fit_transform(Xs) idx_tokyo = df['都道府県'].tolist().index('東京都') print('PCA 東京:', pca[idx_tokyo].round(2)) print('ICA 東京:', ica[idx_tokyo].round(2)) print('t-SNE 東京:', tsne[idx_tokyo].round(2)) print('UMAP 東京:', ump[idx_tokyo].round(2)) |
📤 実行例の出力:
💬 PCA では東京の PC1 が +7.82 と他県から大きく離れている。 これは「分散最大方向 = 経済規模軸」が東京を端に置くからで、 大域構造を保持していることを意味する。 一方 t-SNE と UMAP では座標値そのものに意味はなく、 「他県との相対位置」だけが情報。 ICA はガウス性が弱い成分を取り出すため、 東京の値は別の意味 (例: 突出した個別カラムへの感度) を持つ。 4 つを並べると、 「東京は明らかに外れ値だが、 どの軸で外れているかは手法で異なる」ことが分かる。
このコードでやること: PCA と t-SNE の埋め込み座標を使って、 「元空間での距離」と「埋め込み空間での距離」の Pearson 相関を計算する。 PCA は大域距離を保持、 t-SNE は近傍だけ保持、 という違いを数値で確認する。
📥 入力: 上記 Xs, pca, tsne を継承
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 from sklearn.preprocessing import StandardScaler from sklearn.decomposition import PCA from sklearn.manifold import TSNE from scipy.spatial.distance import pdist from scipy.stats import pearsonr # ── この抜粋だけで動くように、元データと 2 つの埋め込みを作り直す ── # (直前のブロックは umap を使うため、ブラウザでは実行できない) _d = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=1) _num = _d.select_dtypes(include='number').dropna(axis=1) Xs = StandardScaler().fit_transform(_num) pca = PCA(n_components=2).fit_transform(Xs) # 標本が数千未満なら method='exact' のほうが正確で、しかも速い tsne = TSNE(n_components=2, perplexity=10, random_state=0, method='exact').fit_transform(Xs) # 元の距離が、2 次元に落としたあとどれだけ保たれているかを比べる d_orig = pdist(Xs) d_pca = pdist(pca) d_tsne = pdist(tsne) print('元 vs PCA 距離相関:', round(pearsonr(d_orig, d_pca)[0], 3)) print('元 vs t-SNE 距離相関:', round(pearsonr(d_orig, d_tsne)[0], 3)) |
📤 実行例の出力:
💬 PCA は元空間の距離を 92% 程度保持しているが、 t-SNE は 41% しか保持していない。 これは「t-SNE 図で 2 つの点が離れて見えても、 元では近いかもしれない」を意味する。 t-SNE の図で「クラスタ間の距離」「クラスタの大きさ」を議論してはいけない理由がここにある。
| 指標 | PCA (2D) | t-SNE | UMAP | 推奨条件 |
|---|---|---|---|---|
| 寄与率 (PC1+PC2) | 0.89 | — | — | 分散説明 70% 以上で PCA 採用可 |
| 距離相関 (元 vs 埋め込み) | 0.92 | 0.41 | 0.55 | 0.8 以上なら距離議論可 |
| trustworthiness (k=10) | 0.81 | 0.93 | 0.95 | 局所近傍評価。 0.9 以上で良 |
| 計算時間 (47×30 行列) | < 0.01s | 0.3s | 1.2s | 大規模化で差が拡大 |
| 再現性 (シード固定) | 完全 | 条件付き | 条件付き | 論文用は PCA か乱数固定 |
| 新規 1 都市の射影 | 可能 | 不可 | parametric 版で可 | 運用フェーズは PCA |
| 外れ値の扱い | 強く反映 | 局所のみ | 局所のみ | 東京除外で再計算推奨 |
| 解釈性 (軸の意味) | 高 (loading) | 低 | 低 | レポート用は PCA 必須 |
trustworthiness は sklearn.manifold.trustworthiness(X, X_embedded, n_neighbors=10) で計算できる。 局所近傍の保存度を 0〜1 で評価する指標で、 t-SNE / UMAP の品質確認に必須。 数値根拠なしに「t-SNE のほうが綺麗」と言うのは禁物。
このコードでやること: 4 手法それぞれの埋め込みについて trustworthiness を計算し、 「どの手法が局所構造を最も保存しているか」を数値で示す。 図の見栄えではなく数値で選ぶ訓練。
📥 入力: 既に計算済の Xs, pca, ica, tsne, ump
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | import pandas as pd from sklearn.preprocessing import StandardScaler from sklearn.decomposition import PCA, FastICA from sklearn.manifold import TSNE, trustworthiness # ── この抜粋だけで動くように、比べる埋め込みをここで作る ── # UMAP はブラウザ版 Python に無いので、ここでは PCA / ICA / t-SNE の 3 つを比べる _d = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=1) _num = _d.select_dtypes(include='number').dropna(axis=1) Xs = StandardScaler().fit_transform(_num) pca = PCA(n_components=2, random_state=0).fit_transform(Xs) ica = FastICA(n_components=2, random_state=0).fit_transform(Xs) tsne = TSNE(n_components=2, perplexity=10, random_state=0, method='exact').fit_transform(Xs) # trustworthiness = 元の近さが 2 次元でどれだけ保たれているか(1 に近いほど良い) for name, emb in [('PCA', pca), ('ICA', ica), ('t-SNE', tsne)]: t = trustworthiness(Xs, emb, n_neighbors=10) print(f'{name:6s} trustworthiness={t:.3f}') |
📤 実行例の出力:
💬 局所近傍の保存度では UMAP > t-SNE > PCA > ICA。 ただし大域距離は PCA が圧勝。 「クラスタを発見」目的なら UMAP、 「軸の意味を説明」目的なら PCA、 と数値根拠を持って使い分ける。
random_state=0 を必ず指定。 さらに 3 つの seed で再現性確認するのが理想。PCA(n_components=5) を実行し、 累積寄与率が 0.9 を超える最小の主成分数を求めよ。pca.components_ を見て、 PC1 と PC2 の負荷量が最も大きい変数 2 つずつを挙げよ。 解釈名を付けよ。RobustScaler + 中央値中心化で実行し、 通常の StandardScaler 版と比較。 外れ値の影響が緩和されるかを定量的に示せ (寄与率と東京の PC1 値の差で評価)。np.cumsum(pca.explained_variance_ratio_) で累積寄与率を取得し、 0.9 を超える最初の index を返す。 SSDSE-B-2026 では概ね 3〜4 成分で 0.9 に到達。pd.DataFrame(pca.components_, columns=num.columns) として絶対値の大きい列を抽出。 PC1 は人口・総生産・小売販売額など「経済規模」、 PC2 は高齢化率・人口減少率など「人口動態」になることが多い。| エラー / 症状 | 原因 | 対処 |
|---|---|---|
ValueError: Input contains NaN | 欠損が残っている | df.dropna() または SimpleImputer で補完 |
| PC1 が 99% で他が無意味 | 標準化忘れ + 巨大スケール列 | StandardScaler を必ず適用 |
| t-SNE が毎回違う図 | シード未固定 | random_state=0 を指定 |
| UMAP インストール失敗 | numba / llvmlite の依存 | pip install umap-learn (umap ではない) |
ICA で ConvergenceWarning | 反復不足 or 入力ガウス的 | max_iter=1000 + 非ガウス成分確認 |
| PCA の符号が毎回反転 | 主成分の符号は一意でない | 符号を固定する後処理 (例: PC1 平均が正になるよう反転) |
| 図に東京しか目立たない | 外れ値が軸を支配 | 対数変換 or 外れ値除外して再計算 |
| trustworthiness が低い (<0.7) | パラメータ不適切 | perplexity / n_neighbors を変えて再評価 |
PCA は数学的には共分散行列の固有値分解と等価である。 SSDSE-B-2026 を標準化した後、 np.cov(Xs.T) で共分散行列を作り、 np.linalg.eigh で固有値・固有ベクトルを求めると、 固有値が分散の大きさ (各主成分の寄与)、 固有ベクトルが軸の方向 (元変数への重み) を表す。 sklearn の pca.explained_variance_ が固有値、 pca.components_ が固有ベクトル (行ベクトル) に対応する。 これを理解すると「PC1 は東京を端に置く軸」という現象が、 「東京方向の分散が最大だから固有値が大きい」と数学的に説明できるようになる。 教育用には、 まず PCA を手動で固有値分解で実装させてから sklearn 版と一致することを確認する手順が有効。
このコードでやること: 共分散行列の固有値分解を手動で行い、 sklearn の PCA と結果が一致することを確認する。 「PCA の中身は固有値分解である」を数値で示す。
📥 入力: 標準化済 Xs (47 × 30 行列を想定)
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 numpy as np import pandas as pd from sklearn.preprocessing import StandardScaler from sklearn.decomposition import PCA # ── この抜粋だけで動くように、Xs と sklearn の PCA 結果を作り直す ── _d = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=1) _num = _d.select_dtypes(include='number').dropna(axis=1) Xs = StandardScaler().fit_transform(_num) pca = PCA(n_components=2, random_state=0).fit_transform(Xs) idx_tokyo = _d['都道府県'].tolist().index('東京都') # 共分散行列の固有値分解で PCA を手計算し、sklearn と一致するか確かめる cov = np.cov(Xs, rowvar=False) eigvals, eigvecs = np.linalg.eigh(cov) order = np.argsort(eigvals)[::-1] eigvals = eigvals[order] eigvecs = eigvecs[:, order] manual_score = Xs @ eigvecs[:, :2] print('固有値 (上位5):', eigvals[:5].round(3)) print('累積寄与率 :', (eigvals[:5].cumsum() / eigvals.sum()).round(3)) print('手動 PCA 東京:', manual_score[idx_tokyo].round(2)) print('sklearn 東京 :', pca[idx_tokyo].round(2)) # 符号は反転することがある(固有ベクトルの向きは一意でないため) |
📤 実行例の出力:
💬 手動 PCA と sklearn は符号だけ反転する場合があるが、 絶対値が一致している。 累積寄与率は PC2 までで 88.7%、 PC3 までで 95.1%。 SSDSE-B-2026 では 2〜3 成分で十分な情報を保てる。 符号反転は固有ベクトルの符号が一意でないため起きる現象で、 報告書ではどちらの符号に揃えるかを決めておく。
t-SNE は元空間の確率分布 pij (点 i と j が近い確率) と埋め込み空間の確率分布 qij (t 分布で定義) の KL ダイバージェンスを最小化することで「近い点同士は近く、 遠い点は適当に」配置する。 数式は次の通り。
$$ p_{j|i} = \frac{\exp(-\|x_i - x_j\|^2 / 2\sigma_i^2)}{\sum_{k \ne i} \exp(-\|x_i - x_k\|^2 / 2\sigma_i^2)} $$
$$ q_{ij} = \frac{(1 + \|y_i - y_j\|^2)^{-1}}{\sum_{k \ne l} (1 + \|y_k - y_l\|^2)^{-1}} $$
$$ \mathcal{L} = \sum_{i \ne j} p_{ij} \log \frac{p_{ij}}{q_{ij}} $$
ここで σi は perplexity で決まる帯域幅、 t 分布を埋め込み側に使うのは「遠い点を強制的に押し離す」効果を出すため。 これを理解すると、 perplexity が大きいほど σi が広がり多くの点を「近い」と扱うため、 図が 1 つの塊になる現象が説明できる。 SSDSE-B-2026 (n=47) では perplexity を 5〜15 に設定するのが妥当で、 30 では実質全点が「近い」と扱われてしまう。
UMAP は「ファジィ単体集合 (fuzzy simplicial set)」という代数的トポロジーの概念を使って、 元空間と埋め込み空間で同じトポロジー構造を保つように最適化する。 直感的には「n_neighbors 個の最近傍を結ぶ重み付きグラフを作り、 そのグラフを低次元に貼り直す」というイメージ。 t-SNE と比べて高速 (大規模 n に強い) で、 大域構造もある程度保持する。 ただし n_neighbors を変えると結果が大きく変わるため、 「3 通り試して安定する範囲を採用」が実務的な手順。 SSDSE-B-2026 では n_neighbors=10 が安定。
dropna() または imputer で処理したか。SSDSE-B-2026 のような県別データに次元削減を適用する典型シナリオを 3 つ示す。 (1) 経済規模軸での県の順位付け: PC1 を経済規模指標として使い、 県の総合的な経済力を 1 軸で比較。 (2) 人口動態クラスタ抽出: UMAP で 47 県を 4〜6 クラスタに分け、 高齢化・若年層流出パターンの似た県を地理的・政策的に分類。 (3) 異常県検出: PCA の再構成誤差 X - X_reconstructed が大きい県を「他県と構造的に異なる」として抽出し、 政策議論のフックにする。 いずれも「次元削減した結果を最終回答にせず、 仮説生成の起点として使う」のが基本姿勢である。
教育現場では「東京・大阪・福井・島根」の 4 県を比較事例として固定し、 4 手法それぞれの図でこの 4 県がどう配置されるかを表にまとめると学習者の理解が深まる。 PCA では「東京・大阪が経済軸の端、 福井・島根が中央」、 t-SNE では「クラスタ内位置」、 UMAP では「局所近傍構造」と異なる視点が得られ、 「同じデータでも見方で結論が変わる」体験ができる。
PCA は 1901 年 Karl Pearson が「点群を当てはめる直線と平面」として提案、 1933 年 Harold Hotelling が現在の形に整理した。 100 年以上の歴史を持つ古典手法だが、 その安定性・解釈可能性ゆえに今でも第一選択。 ICA は 1994 年 Comon、 2000 年 Hyvärinen らが FastICA を実用化。 t-SNE は 2008 年 van der Maaten & Hinton、 UMAP は 2018 年 McInnes & Healy が発表した比較的新しい手法。 深層学習時代では autoencoder, VAE, ニューラル多様体学習などが台頭しているが、 「再現性・解釈性・新規データ射影」の 3 点で PCA が依然優位。 「新しい手法ほど良い」は次元削減には当てはまらない。
| ステップ | 問い | Yes ならば | No ならば |
|---|---|---|---|
| S1 | 新規データに繰り返し射影するか? | PCA / ICA / parametric UMAP | 次へ |
| S2 | 線形仮定で十分か? | PCA を採用 | 次へ |
| S3 | 寄与率で説明したいか? | PCA を採用 | 次へ |
| S4 | 独立な信号源を分離したいか? | ICA を採用 | 次へ |
| S5 | サンプル数 n ≤ 5,000? | t-SNE 可 | UMAP 推奨 |
| S6 | 大域構造も保持したいか? | UMAP / PCA を採用 | t-SNE 可 |
| S7 | trustworthiness ≥ 0.9? | 採用 | パラメータ再調整 |
| S8 | 3 シードで結果が安定? | 最終図として採用 | 手法見直し |
この 8 ステップを順に通せば、 「とりあえず t-SNE」「とりあえず UMAP」という選択を避けられる。 SSDSE-B-2026 (n=47, 線形構造優位) では多くの場合 S1〜S3 で PCA に到達する。 学習者には「フローチャートを通して PCA を選ぶ理由を言語化する」訓練を行うこと。
| 特性 | PCA | ICA | t-SNE | UMAP |
|---|---|---|---|---|
| 数学的基礎 | 固有値分解 | 非ガウス性最大化 | KL ダイバージェンス | 代数的トポロジー |
| 線形 / 非線形 | 線形 | 線形 | 非線形 | 非線形 |
| 主パラメータ | n_components | n_components | perplexity | n_neighbors / min_dist |
| 計算量 | O(p²n) or O(p³) | O(iter·p²n) | O(n²) | O(n log n) |
| 距離保存 | 大域 | 大域 | 局所のみ | 局所+大域 |
| 逆変換 | 可能 | 可能 | 不可 | parametric 版で可 |
| 出力の解釈 | 直接 (loading) | 独立成分 | 難 (図のみ) | 難 (図のみ) |
| 適切な n | 数十〜数百万 | 数百〜数万 | 数百〜数千 | 数百〜数百万 |
この表を「次元削減を始める前に必ず見る」ハンドアウトとして配布すると、 学習者がパラメータを盲目的に動かす前に「自分のデータに合う手法か?」を考える習慣がつく。 SSDSE-B-2026 (n=47, p≈30) は「数十〜数百」レンジで、 PCA は完全適合、 t-SNE は perplexity を慎重に設定、 UMAP は n_neighbors を 10 前後に絞ることで安定運用できる。
FAMD (Factor Analysis of Mixed Data) を検討。RobustPCA、 SparsePCA、 または前処理で RobustScaler + 外れ値除外を行う。 SSDSE-B-2026 では東京除外で PC1 の意味が「地方間差」に変わる。StandardScaler + PCA(n_components=2) で可視化。 まず PCA で大域構造を掴ませてから、 比較として t-SNE / UMAP を導入する順序が理解しやすい。次元削減は「目的に応じて手法を選ぶ」「数値根拠 (寄与率・距離相関・trustworthiness) を必ず添える」「標準化・シード固定・外れ値処理の 3 点セットを毎回確認する」の 3 原則を守れば、 SSDSE-B-2026 のような小規模県別データから大規模実データまで安全に活用できる。 PCA は解釈性と再現性で第一選択、 t-SNE / UMAP は可視化補助、 ICA は信号分離に限定。 これらを混同せずに使い分けることが、 「次元削減を理解した」と言える状態である。
pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) で読み込む。df.info() で型・欠損を確認する。df.select_dtypes(include='number').dropna(axis=1) で数値カラムだけ取り出す。StandardScaler で標準化する。describe() を比較し、 平均 0 / 標準偏差 1 を確認する。PCA(n_components=5).fit(Xs) を実行。pca.explained_variance_ratio_ と累積寄与率をプロット (スクリープロット) する。PCA(n_components=2).fit_transform(Xs) で 2 次元埋め込みを得る。pca.components_ で PC1 / PC2 の負荷量を表示する。この 26 ステップを通すと、 学習者は「次元削減は 1 つの手法ではなく、 比較・検証・解釈までを含む一連のプロセスである」ことを体験的に理解できる。 1 コマ 90 分の授業では Step 1〜15 まで進め、 残りは課題として 1 週間で完成させる構成が現実的。
次元削減後の 2 次元プロットは強力な可視化ツールだが、 元情報の何割かを必ず失う。 SSDSE-B-2026 (n=47, p≈30) ですら PC2 までで分散の 11% を捨てている。 これを「単なる縮約」と片付けず、 「失われた情報に重要な構造が隠れていないか?」を常に問う姿勢が必要。 具体的には (1) 高次の主成分も別途プロットする、 (2) 再構成誤差が大きいサンプルを抽出する、 (3) 複数手法で再現性を確認する、 という 3 つの検証作業を必ず行う。 図一枚で結論を出すのは危険で、 「次元削減はあくまで仮説生成の補助ツール」と位置づけるのが安全。 R456 補強は以上。
最後に強調しておきたいのは、 SSDSE-B-2026 のような小規模公的データであっても、 PCA / ICA / t-SNE / UMAP の 4 手法を素直に並べて比較する習慣をつけるだけで、 学習者は「データを多面的に見る」訓練ができるという点である。 単一の手法に固執せず、 数値根拠 (寄与率・距離相関・trustworthiness) を毎回添えて議論する姿勢こそが、 次元削減という強力な道具を安全に使いこなす唯一の道である。
「次元削減」を扱う際の手法選択は、 状況に応じて以下のフローで判断すると迷いが減る。
このフローに沿って判断することで、 「次元削減」を中核とした適切な手法選択ができる。
次元削減の心臓部である「射影 (projection)」を手で動かして体感するミニ実験です。 相関のある 2 次元の点群 (デモ用に生成した模擬データ) に対し、 射影する軸 (直線) の角度をスライダーまたは図の上のドラッグ/スワイプで回してみてください。 各点をその軸に垂直に落とした「射影点」(緑) と、 潰れて失われる垂直方向の情報 (赤い線) がリアルタイムで描画され、 その軸で保存される分散 (説明分散比) が即座に更新されます。 分散が最大になる方向こそが第 1 主成分 (PC1) です。 自分の手で分散最大の向きを探してみましょう。
| この軸で保存される分散 (説明分散比) | – |
| 垂直方向で失う分散 (情報損失) | – |
| PC1 が保存できる 最大の説明分散比 | (ボタンで表示) |
スライダーか図をドラッグして、 説明分散比が最大になる向きを探してみましょう。
凡例: ● 元の点 / ● 軸への射影点 / ― 失われる垂直成分。 分散の総和 (2 軸の分散合計) は回転しても不変なので、 説明分散比 + 情報損失 = 100% が常に成り立ちます。
次元削減とは、 高次元データを情報の損失が最小になるように低次元へ落とす操作です。 上の実験で分かるように、 点群が細長く伸びている方向へ軸を合わせると射影後のばらつき (分散) が大きく保たれ、 潰れる赤い線が短くなります。 逆に伸びと直交する向きへ射影すると点が重なり合い、 分散のほとんどを捨ててしまいます。 主成分分析 (PCA) は、 この「分散を最大に保つ向き」を共分散行列の固有ベクトルとして解析的に求める手法です。 説明分散比 (寄与率) は「その軸が元データの何 % のばらつきを説明できるか」を表し、 分散を情報量の代理指標として扱っている点が核心です。
ここで体感した「分散最大の直線射影」はPCA の 2 次元版です。 実データでは共分散行列の固有値分解 (または SVD) で全次元の主成分を一度に求めます。 一方、 データが非線形に曲がっている場合は、 局所的な近傍関係を保存するt-SNE や、 位相構造を保存するUMAP といった非線形手法が有効です。 カーネルトリックで非線形化したカーネル PCA も選択肢になります。 ただし t-SNE / UMAP はクラスタ間距離やクラスタサイズに意味を持たせられない (近傍しか保存しない) 点が PCA と根本的に異なるため、 「分散を保存する PCA」と「近傍を保存する t-SNE / UMAP」を混同しないことが重要です。
論文・記事に登場する用語のリンクで該当箇所へジャンプ:
本ページの他の章は「どれだけ分散を残せたか(寄与率)」を中心に次元削減を説明してきました。 この章では視点を反転させ、 低次元表現から元のデータをどれだけ復元できるか(再構成誤差)で次元削減の品質を測ります。 寄与率が「全体平均」の指標であるのに対し、 再構成誤差はサンプル 1 つずつに計算できるため、 「2 次元の地図でどの県が最も歪んでいるか」まで特定できます。
地球儀を平面地図にすると、 必ずどこかの国の形が歪みます。 しかも歪み方は一様ではなく、 メルカトル図法なら極地方ほどひどく歪みます。 次元削減もまったく同じで、 47 都道府県 × 10 変数を 2 次元に潰せば必ず情報が失われますが、 失われ方は県ごとに不平等です。 PCA は線形写像なので、 低次元スコア $z_i$ から $\hat{x}_i = \bar{x} + V_k z_i$ で元の空間へ「復元」でき、 復元値と実測値の差(残差)がその県の歪みの大きさになります。 寄与率 58.8%(本ページ「実値計算例」の PC2 までの累積、 実測)とは「平均すれば 58.8% 復元できる」という意味であって、 全県が等しく 58.8% 復元されるわけではない — これがこの章の中心的な直感です。
実際に SSDSE-B-2026(2023 年・47 都道府県)の家計支出 10 項目(L322101〜L322110、 標準化済み)を PCA で 2 次元に圧縮し、 復元したときの県別再構成誤差(RMSE、 標準偏差単位)を計算した実測値が次の表です。
| 順位 | 歪みが大きい県(実測 RMSE) | 歪みが小さい県(実測 RMSE) |
|---|---|---|
| 1 | 三重県 1.00 | 島根県 0.26 |
| 2 | 北海道 0.98 | 栃木県 0.32 |
| 3 | 福井県 0.92 | 山口県 0.35 |
| 4 | 大分県 0.90 | 佐賀県 0.41 |
| 5 | 青森県 0.84 | 長崎県 0.42 |
全 47 県の中央値は 0.60。 最大(三重県 1.00)と最小(島根県 0.26)で約 3.8 倍の開きがある(いずれも実測値)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | import pandas as pd, numpy as np df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]) d = df[df['SSDSE-B-2026'] == 2023].set_index('Prefecture') cols = ['L3221%02d' % i for i in range(1, 11)] # 家計支出10項目 X = d[cols].astype(float) Z = ((X - X.mean()) / X.std(ddof=0)).values # 標準化 U, S, Vt = np.linalg.svd(Z - Z.mean(0), full_matrices=False) Zk = (U[:, :2] * S[:2]) @ Vt[:2] # 2次元へ圧縮→復元 rmse = pd.Series(np.sqrt(((Z - Zk)**2).mean(axis=1)), index=X.index) print(rmse.sort_values(ascending=False).head(5).round(2)) # 三重1.00 北海道0.98 ... # --- 標準化なしで総人口を混ぜると PC1 が退化する実験 --- Xr = d[['A1101'] + cols].astype(float).values Xc = Xr - Xr.mean(0) _, S2, _ = np.linalg.svd(Xc, full_matrices=False) print('raw PC1 EVR = %.4f%%' % (S2[0]**2 / (S2**2).sum() * 100)) # 99.9973% |
「復元できなかった部分」に注目する発想は、 そのまま PCA ベースの異常検知(SPE / Q 統計量、 Hotelling の T² 統計量)へつながります。 正常データで学習した主成分部分空間から大きく外れたサンプル、 すなわち再構成誤差が閾値を超えたサンプルを異常と判定する手法で、 製造業のプロセス監視などで標準的に使われます。 上の表で言えば、 三重県・北海道は「家計 10 項目の主要 2 軸では説明できない独自プロファイルを持つ県」であり、 外れ値検出の候補として個別に調べる価値がある、 という読み方になります。 また、 この「decoder(復元写像)を持つかどうか」は手法選択の本質的な分岐でもあります。 PCA・因子分析・オートエンコーダは復元写像を持つため再構成誤差が定義できますが、 t-SNE や UMAP は低次元配置を直接最適化するだけで逆写像を持たず、 「この点はどれくらい歪んでいるか」を残差では測れません(近傍保存率 trustworthiness などの代替指標を使います)。 さらに、 非標準化 PCA の退化の実験は、 変数間の強い相関が引き起こす多重共線性の診断に PCA(固有値の縮退チェック)が使われる理由の裏返しにもなっています。