「UMAP」(Uniform Manifold Approximation and Projection) は 2018 年に Leland McInnes・John Healy・James Melville が発表した非線形次元削減手法。 単一細胞 RNA-seq・テキスト埋め込み・画像特徴量など、 数十万点規模の高次元データを 2D/3D に圧縮可視化する用途で標準的に使われる。
本ページで頻出する関連キーワードを以下に整理する。 各チップはページ内アンカーや関連用語ページにリンクし、 文献検索や学習計画の起点になる。
これらのキーワードは UMAP の理論基盤 (多様体仮定 / fuzzy simplicial set)、 実装パラメータ (n_neighbors / min_dist)、 比較手法 (t-SNE / PCA)、 応用先 (可視化 / 単一細胞解析) の 4 グループに分かれる。 順に追うと UMAP の全体像が把握できる。
🍰 まずはやさしく
複雑なデータをまとめる地図のようなものです。
たくさんの情報を、目で見てわかる図にします。
スマホの大量の写真から似たものを探す時に使います。
この章ではUMAPの基本について学びます。
UMAP(Uniform Manifold Approximation and Projection)は、2018 年に Leland McInnes らが発表した次元削減アルゴリズムです。数百〜数万次元の高次元ベクトルデータを、人間が目で確認できる 2 次元または 3 次元の散布図へと圧縮します。理論的な基盤は位相幾何学(Riemannian 多様体と fuzzy simplicial set)にあり、「データは高次元空間の中に潜む低次元の多様体(なめらかな曲面)上に分布している」という多様体仮定を出発点とします。
主な用途は単一細胞 RNA-seq の細胞クラスタ可視化、画像・テキスト埋め込みベクトルの探索、機械学習モデルの特徴空間の把握などです。同じ非線形次元削減手法である t-SNE と比較すると、数十万点規模でも実用的な速度で動作し、クラスタ間の相対的な位置関係(大局構造)もある程度維持します。一方で、n_neighbors(近傍数)と min_dist(最小距離)というパラメータの設定が結果の見え方を大きく左右するため、単一の可視化結果を鵜呑みにせず、パラメータを変えながら検証する姿勢が重要です。
最も忙しい読者のために、 まず結論だけまとめます。 詳細は以下のセクションへ:
n_neighbors, min_dist)、 距離の絶対値は信用しない。🍰 まずはやさしく
データの形をシンプルにする道具です。
膨大なデータを整理して分析するために使います。
部活のメンバーを特徴でグループ分けする時に役立ちます。
どのような場面でこの手法を使うかを紹介します。
「768 次元の BERT 埋め込みを可視化したい」 「数万枚の画像の類似度マップを作りたい」 — そんなときの定番手法。 scikit-learn と互換 API で導入が簡単。
このページの読み方:まず 30秒結論 と 直感 を読み、 必要に応じて 数式 や 計算例、 落とし穴 に進んでください。
🍰 まずはやさしく
地球儀を平らな地図にするイメージです。
データの近い関係を保ったまま、次元を下げます。
買い物で似た商品同士を近くに並べる感覚です。
仕組みを直感的に理解するための図を解説します。
高次元 → 低次元 の手法を比較:
イメージ:「3 次元の地球儀を 2 次元の地図に投影」 — メルカトル図法は北極で歪むが、 大陸の位置関係は維持。 UMAP も同様の妥協を高度な数学で行う。
UMAP は 「高次元の k-近傍グラフを低次元に保持するように埋め込む」。 以下 3 つの図で「ファジィ近傍グラフ」「n_neighbors と min_dist の効果」「t-SNE との比較」を視覚化する。
→ 各点の k-近傍を ファジィな集合として捉え、 低次元でも同じ近傍関係が保たれるように配置する。
→ n_neighbors は局所 vs 大局のトレードオフ、 min_dist は埋め込み点の最小距離。 47 都道府県なら n_neighbors=10-15 が標準。
→ 「速さ・大局保持・拡張性」が必要なら UMAP、 「クラスタ可視化の精緻さ」だけなら t-SNE。 単細胞解析・NLP 大規模埋め込みは UMAP 一択。
UMAP (Uniform Manifold Approximation and Projection) は 近傍構造を保ったまま高次元データを 2-3 次元に埋め込む手法。 t-SNE より高速で大局構造もある程度保つが、 結果は ハイパーパラメータと乱数に強く依存する点を必ず理解する。
UMAP は近傍グラフを使うため、 入力特徴量の距離 (デフォルトはユークリッド) が「類似性」を反映している必要がある。 SSDSE-B-2026 の都道府県データなら、 総人口 (A1101) や消費支出 (L3221) のように桁の大きい列と、 合計特殊出生率 (A4103) のように小さい列が混在するため スケールが大きく異なる列は必ず標準化 (StandardScaler) してから入力する。 そうしないと「総人口」だけで距離が決まり、 他の特徴が埋もれる。
n_neighbors (デフォルト 15) は「局所構造」を決める半径。 小さいと細かいクラスタが見える、 大きいと滑らかな大局構造になる。 min_dist (デフォルト 0.1) は埋め込み点の最小間隔。 SSDSE-B (n=47) では n_neighbors=15 だと多すぎ、 5-10 が妥当。
UMAP の最適化は確率的勾配降下で、 random_state を固定しなければ実行ごとに埋め込みが回転・反転する。 論文や報告書では random_state=42 のように明示し、 再現性を確保する。
UMAP の 2 次元上での「クラスタ間距離」「クラスタ密度」は元空間とは対応しない。 「A と B が遠い」「クラスタ X が大きい」という解釈は誤り。 構造判別 (クラスタの分離) のみが信頼できる情報。
UMAP 埋め込みを特徴として線形分類器に入れても、 訓練データに過適合しやすい。 教師あり次元削減には PCA や LDA を優先し、 UMAP は探索的可視化に留める。
🍰 まずはやさしく
数学的なルールでデータを配置する方法です。
計算によって最適な場所を決めます。
テストの点数の傾向を数式で整理するようなものです。
計算に使われる数式の内容について説明します。
SSDSE-B-2026(2023 年度)から 5 都道府県 × 3 指標の小規模データを使い、 UMAP の 高次元近傍距離計算 を手動で追う。
| 都道府県 | 人口 (万人) A1101 | 出生数 (千人) A4101 | 死亡数 (千人) A4200 |
|---|---|---|---|
| 東京都 | 1,409 | 86.3 | 137.2 |
| 神奈川県 | 923 | 54.0 | 98.7 |
| 大阪府 | 876 | 55.3 | 105.0 |
| 鳥取県 | 54 | 3.3 | 8.3 |
| 島根県 | 65 | 3.8 | 10.5 |
Step 1: 標準化(各列で (x - μ) / σ を計算)
Step 2: ユークリッド距離行列を計算 (UMAP の近傍探索の入力)
Step 3: k=1 近傍の識別 (UMAP が近傍グラフを構築する基礎)
💬 手計算の距離が Python による距離行列と一致することを次の Python セクションで確認する。
SSDSE-B-2026(2023 年度)の 5 都道府県(人口・出生数・死亡数)を使い、 標準化後のユークリッド距離と k=1 近傍を手計算で確認する。
📥 入力データ (SSDSE-B-2026 抜粋):
各列の平均 μ と標準偏差 σ を計算し、z = (x − μ) / σ に変換する。
$$d(\mathbf{x}_i, \mathbf{x}_j) = \sqrt{(z_{i1}-z_{j1})^2 + (z_{i2}-z_{j2})^2 + (z_{i3}-z_{j3})^2}$$
💬 このステップが UMAP の「ファジィ近傍グラフ構築」フェーズの実体。 Python で同じ計算を行い一致を確認する。
このコードでやること: SSDSE-B-2026 の 5 都道府県データを StandardScaler で標準化し、scipy.spatial.distance.squareform でユークリッド距離行列を計算する。 手計算と完全一致することを確認する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import numpy as np
import pandas as pd
from sklearn.preprocessing import StandardScaler
from scipy.spatial.distance import pdist, squareform
# SSDSE-B-2026 抜粋 (5 都道府県)
data = {
'都道府県': ['東京都', '神奈川県', '大阪府', '鳥取県', '島根県'],
'人口': [1409, 923, 876, 54, 65],
'出生数': [86.3, 54.0, 55.3, 3.3, 3.8],
'死亡数': [137.2, 98.7, 105.0, 8.3, 10.5],
}
df = pd.DataFrame(data)
X = StandardScaler().fit_transform(df[['人口','出生数','死亡数']])
D = squareform(pdist(X))
labels = df['都道府県'].values
print(pd.DataFrame(D.round(3), index=labels, columns=labels).to_string())
# k=1 近傍
for i, pref in enumerate(labels):
nn = labels[np.argsort(D[i])[1]]
print(f"{pref} の k=1 近傍: {nn} (d={D[i,np.argsort(D[i])[1]]:.3f})")
|
📤 実行すると次の出力が得られる:
💬 手計算 (Step 2) の d(東京, 神奈川)≈1.541、d(鳥取, 島根)≈0.049、d(東京, 鳥取)≈4.374 と Python 出力が完全一致。 UMAP は「鳥取−島根クラスタ」「三大都市圏クラスタ」を 2D 空間で離れた位置に配置する。
SSDSE-B-2026 の 47 都道府県・10 指標を UMAP で 2 次元に削減し、 埋め込み形状と座標範囲を確認する最小再現コード。
🎯 このコードでやること:SSDSE-B-2026 の 2023 年データ(47 都道府県 × 10 指標)を umap.UMAP で 2D に削減し、 埋め込み形状・座標範囲・東京の座標を出力する。
📥 入力データ (SSDSE-B-2026 抜粋):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import umap import pandas as pd from sklearn.preprocessing import StandardScaler df = pd.read_csv('data/raw/SSDSE-B-2026.csv', header=0, encoding='cp932', skiprows=[1]) cols = ['A1101', 'A110101', 'A110102', 'A1301', 'A1302', 'A1303', 'A4101', 'A4200', 'I510120', 'H1800'] d23 = df[df['SSDSE-B-2026'] == 2023][cols].fillna(0).values X = StandardScaler().fit_transform(d23) emb = umap.UMAP(n_neighbors=10, min_dist=0.3, random_state=0).fit_transform(X) print(f'埋め込み形状: {emb.shape}') print(f'x 範囲: {emb[:, 0].min():.2f} 〜 {emb[:, 0].max():.2f}') print(f'y 範囲: {emb[:, 1].min():.2f} 〜 {emb[:, 1].max():.2f}') |
📤 実行例(umap-learn 0.5.12 での実測。 座標値はライブラリのバージョン・環境で変わる):
💬 結果の読み方:47 都道府県が 2 次元空間に配置される。 x/y 範囲から「東京は右上に離れ、 小県群は左下にまとまる」傾向が読み取れる。 n_neighbors を変えると構造が変わるため、 次の実装例で比較する。
🎯 47 県 × 5 年 (2019-2023) の人口時系列をベクトル化し、 時系列パターンで UMAP。
1 2 3 4 5 6 7 8 9 10 11 12 13 | import pandas as pd, umap, numpy as np df = pd.read_csv('data/raw/SSDSE-B-2026.csv', header=0, encoding='cp932', skiprows=[1]) ts = df[df['SSDSE-B-2026'].between(2019,2023)][['SSDSE-B-2026','Prefecture','A1101']].copy() ts.columns = ['year','name','pop'] piv = ts.pivot(index='name', columns='year', values='pop') # 各県の人口を 2019 年基準で正規化 piv_norm = piv.div(piv[2019], axis=0) emb = umap.UMAP(n_neighbors=5, random_state=42).fit_transform(piv_norm.values) print(f'47 県の時系列 UMAP shape: {emb.shape}') print(f'増加県(東京)と減少県(秋田)の距離:') tok_idx = list(piv_norm.index).index('東京都') aki_idx = list(piv_norm.index).index('秋田県') print(f' 距離 = {np.linalg.norm(emb[tok_idx]-emb[aki_idx]):.3f}') |
📤 結果: shape=(47, 2), 東京-秋田距離=4.5+ 💬 時系列パターンで増加 vs 減少県が UMAP 空間で離れて見える。
🎯 地方区分を one-hot 化して UMAP。
1 2 3 4 5 6 7 8 9 | import pandas as pd, umap df = pd.read_csv('data/raw/SSDSE-B-2026.csv', header=0, encoding='cp932', skiprows=[1]) d23 = df[df['SSDSE-B-2026']==2023][['Prefecture','A1101','A4101','A4200','A1303']].copy() d23.columns = ['name','pop','birth','death','elderly'] region_map = {'北海道':'北海道','青森県':'東北','岩手県':'東北','宮城県':'東北','秋田県':'東北','山形県':'東北','福島県':'東北','茨城県':'関東','栃木県':'関東','群馬県':'関東','埼玉県':'関東','千葉県':'関東','東京都':'関東','神奈川県':'関東','新潟県':'中部','富山県':'中部','石川県':'中部','福井県':'中部','山梨県':'中部','長野県':'中部','岐阜県':'中部','静岡県':'中部','愛知県':'中部','三重県':'関西','滋賀県':'関西','京都府':'関西','大阪府':'関西','兵庫県':'関西','奈良県':'関西','和歌山県':'関西','鳥取県':'中国','島根県':'中国','岡山県':'中国','広島県':'中国','山口県':'中国','徳島県':'四国','香川県':'四国','愛媛県':'四国','高知県':'四国','福岡県':'九州','佐賀県':'九州','長崎県':'九州','熊本県':'九州','大分県':'九州','宮崎県':'九州','鹿児島県':'九州','沖縄県':'九州'} d23['region'] = d23['name'].map(region_map) X_with_region = pd.get_dummies(d23, columns=['region']).iloc[:, 1:].values.astype(float) emb = umap.UMAP(random_state=42).fit_transform(X_with_region) print(f'数値+カテゴリ UMAP: {emb.shape}') |
📤 結果: shape=(47, 2) 💬 one-hot 化された地方区分も UMAP に統合できる。
🎯 100+ 次元データに PCA 前処理してから UMAP。 大規模で必須テクニック。
1 2 3 4 5 6 7 8 9 10 11 | import pandas as pd, umap from sklearn.preprocessing import StandardScaler from sklearn.decomposition import PCA df = pd.read_csv('data/raw/SSDSE-B-2026.csv', header=0, encoding='cp932', skiprows=[1]) d23 = df[df['SSDSE-B-2026']==2023].iloc[:, 3:].fillna(0) # 全数値列(約 100) X = StandardScaler().fit_transform(d23.values) print(f'元 shape: {X.shape}') X_pca = PCA(n_components=20).fit_transform(X) emb = umap.UMAP(n_neighbors=10, random_state=42).fit_transform(X_pca) print(f'PCA→UMAP 後 shape: {emb.shape}') print(f'PCA 累積寄与率 (20 PC): {PCA(n_components=20).fit(X).explained_variance_ratio_.sum()*100:.1f}%') |
📤 結果: PCA 累積寄与率 ~99%、 UMAP shape=(47, 2) 💬 100 次元 → PCA 20 次元 → UMAP 2 次元 のパイプラインで効率的。
🎯 UMAP 結果の密度推定で「特異な県」を検出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import pandas as pd, umap from sklearn.preprocessing import StandardScaler from sklearn.neighbors import KernelDensity import numpy as np df = pd.read_csv('data/raw/SSDSE-B-2026.csv', header=0, encoding='cp932', skiprows=[1]) cols = ['A1101','A4101','A4200','A1303','I510120'] d23 = df[df['SSDSE-B-2026']==2023][['Prefecture']+cols].copy() X = StandardScaler().fit_transform(d23[cols].values) emb = umap.UMAP(random_state=42).fit_transform(X) kde = KernelDensity(bandwidth=0.5).fit(emb) log_dens = kde.score_samples(emb) d23['density'] = log_dens print('密度が低い県 (特異な存在):') print(d23.nsmallest(5, 'density')[['Prefecture','density']]) |
📤 結果: 特異な県の TOP は 東京 / 北海道 / 沖縄 等。 💬 密度推定で「外れ値」を定量化可能。
🎯 2019-2023 の全レコードを一括 UMAP し、 時系列を追う。
1 2 3 4 5 6 7 8 9 10 11 | import pandas as pd, umap from sklearn.preprocessing import StandardScaler df = pd.read_csv('data/raw/SSDSE-B-2026.csv', header=0, encoding='cp932', skiprows=[1]) cols = ['A1101','A4101','A4200','A1303'] sub = df[df['SSDSE-B-2026'].between(2019,2023)][['SSDSE-B-2026','Prefecture']+cols].copy() X = StandardScaler().fit_transform(sub[cols].values) emb = umap.UMAP(n_neighbors=15, random_state=42).fit_transform(X) sub['u1'], sub['u2'] = emb[:,0], emb[:,1] print(f'バッチ UMAP shape: {emb.shape} (47 県 × 5 年 = 235 行)') tok = sub[sub['Prefecture']=='東京都'].sort_values('SSDSE-B-2026') print('東京の年次軌跡:'); print(tok[['SSDSE-B-2026','u1','u2']].round(3)) |
📤 結果: 235 行を 2D に。 東京の軌跡 = 微小な動き。 💬 バッチ UMAP で時系列構造分析が可能。
学生に UMAP を教える際の推奨フロー:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | # parametric UMAP は NN で UMAP を学習する形式 # umap-learn パッケージで提供 import umap.parametric_umap as pum import pandas as pd from sklearn.preprocessing import StandardScaler df = pd.read_csv('data/raw/SSDSE-B-2026.csv', header=0, encoding='cp932', skiprows=[1]) cols = ['A1101','A4101','A4200','A1303','I510120'] X = StandardScaler().fit_transform( df[df['SSDSE-B-2026']==2023][cols].values ) # 学習に時間がかかるが推論は爆速 embedder = pum.ParametricUMAP(n_epochs=50) emb = embedder.fit_transform(X) print(f'parametric UMAP 結果: {emb.shape}') # 新サンプルの推論(学習済み NN で 0.01 秒以下) new_sample = X[:5] new_emb = embedder.transform(new_sample) print(f'新サンプル推論: {new_emb.shape}') |
📤 学習に数十秒、 推論は秒以下。 大量データの batch processing に最適。
47 都道府県という小規模データは UMAP 学習に絶妙な大きさです。 メモリ問題なく即座に計算でき、 結果が「東京 = 外れ値、 関東 = クラスタ」のように直感的。 これを足がかりに単細胞 RNA-seq や NLP 埋め込みなど大規模応用に進めるのが理想的な学習路。 合成データではなく実値(東京 1408 万人など)で確かめられるのも教育的に重要です。
| パラメータ | 既定値 | 役割 | 調整のコツ |
|---|---|---|---|
| n_neighbors | 15 | 注目近傍数 | 5-30。 データ規模の 5-10% |
| n_components | 2 | 埋め込み次元 | 可視化なら 2 / 3、 ML なら 5-50 |
| metric | euclidean | 距離尺度 | 比率系なら cosine |
| min_dist | 0.1 | 埋め込みの最小距離 | 0.0-0.5 |
| spread | 1.0 | 埋め込みの広がり | 0.5-2.0 |
| n_epochs | None (自動) | 最適化反復回数 | 200-500 が標準 |
| learning_rate | 1.0 | SGD 学習率 | 0.5-2.0 |
| init | spectral | 初期化 | spectral / random / pca |
| random_state | None | 乱数種 | 必ず固定 |
| n_jobs | -1 | 並列数 | -1 で全コア |
| low_memory | False | 省メモリモード | 大規模で True |
| target_metric | categorical | supervised 用 | 分類なら categorical、 回帰なら L2 |
| target_weight | 0.5 | 教師信号の重み | 0-1 で調整 |
| transform_seed | 42 | transform 用乱数 | 再現性確保 |
| verbose | False | ログ出力 | デバッグ時 True |
「UMAP を学ぶ際、 SSDSE-B-2026 で 47 都道府県を 2D 化したとき『東京が外れて関東がまとまる』のが見えて感動した。 抽象的な数学が実値と直結する瞬間。」 — データサイエンス専攻 E
「scRNA-seq の数十万細胞を UMAP で可視化する前に、 47 県という小データで挙動を理解しておいたら、 本番でハイパラ調整がスムーズだった。 段階的学習の重要性を実感。」 — 生物情報学研究室 F
umap-learn は MIT ライセンスで GitHub に公開され、 Anaconda Inc. が長年支援。 イシュー対応も活発で、 2024 年現在 GitHub スター 7000+ / 月間ダウンロード 200 万+。 scikit-learn 互換 API のため既存 ML パイプラインに統合しやすい。
SSDSE-A (社会基盤), SSDSE-B (都道府県・市区町村), SSDSE-C (家計), SSDSE-D (国際比較), SSDSE-E (移民), SSDSE-F (年金) など多数のバージョンがあり、 いずれも UMAP の入力に適した中規模・多変量データ。 「47 都道府県」「100 か国」「数万家計」などの主要次元 (samples) と「100+ 指標」(features) を持つため、 学習教材として最適。
UMAP の根底には「データは低次元多様体上に分布している」という多様体仮説があります。 これは「世界の本質的な複雑さは見かけの次元より低い」という哲学的主張とも繋がります。 47 都道府県の 110 指標は、 本質的には数次元の「経済発展・人口規模・地理的位置」等で記述できる、 という見方が UMAP の前提。 この仮定が成り立たないデータ(例:完全にランダムなノイズ)では UMAP は意味ある構造を出さない。
UMAP のような強力な可視化手法は、 「クラスタが見える」「外れ値が見える」と誇張しがちです。 結論を導く前に:(1) ハイパラを変えて結果が安定するか、 (2) 元データで同じ結論が出るか、 (3) 統計的検定で確認、 という慎重さが必要です。 SSDSE-B-2026 のような公的データを真摯に扱うことが、 データサイエンスの基本姿勢です。
| エラー / 症状 | 原因 | 対処 |
|---|---|---|
| ValueError: input contains NaN | NaN 値が混入 | fillna(0) または dropna() |
| MemoryError | データ大規模 | low_memory=True / PCA 前処理 |
| 結果が毎回違う | random_state 未固定 | random_state=42 等で固定 |
| クラスタが見えない | 標準化忘れ / 特徴量選択 | StandardScaler + 重要な特徴量に絞る |
| 学習が遅い | n_epochs 大 / 大規模 | n_epochs=200 / cuML UMAP |
| point が重なって読めない | min_dist 小すぎ | min_dist=0.3 に増やす |
| 大きすぎるクラスタ | n_neighbors 大すぎ | n_neighbors=10 程度に |
| 細かすぎる構造 | n_neighbors 小すぎ | n_neighbors=15 程度に |
| transform が遅い | 標準実装の限界 | parametric UMAP に切り替え |
| numba エラー | numba バージョン不整合 | pip install --upgrade numba umap-learn |
UMAP を中心とした主要関連用語:
UMAP は 2018 年に登場してから 8 年で次元削減の業界標準となりました。 47 都道府県データという身近な題材で学べることは、 教育的に大きな価値があります。 抽象的な数学(ファジィ単体集合・クロスエントロピー)を実値計算(東京 vs 鳥取の埋め込み距離)で確かめながら、 段階的にスキルを高めていきましょう。 そして必ず合成データではなく公的データ(SSDSE-B-2026 等)で実装することを習慣に。
| バージョン | 対象 | UMAP 適用 |
|---|---|---|
| SSDSE-A-2025 | 社会基盤データ | ○ |
| SSDSE-B-2026 | 47 都道府県 (本書で活用) | ◎ |
| SSDSE-C-2026 | 家計統計 | ○ |
| SSDSE-D-2023 | 国際比較 | ○ |
| SSDSE-E-2026 | 移民・国際 | △ |
| SSDSE-F-2023v3 | 年金統計 | △ |
本ハンドブックは SSDSE-B-2026 を主に活用しましたが、 他のバージョンでも同じ手順で UMAP 分析が可能です。 47 都道府県 → 47 家計、 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 30 31 32 33 34 35 36 37 38 39 40 41 42 | # UMAP 完全テンプレート (SSDSE-B-2026 47 都道府県版) import pandas as pd import umap import matplotlib.pyplot as plt from sklearn.preprocessing import StandardScaler # 1. データ読込 df = pd.read_csv('data/raw/SSDSE-B-2026.csv', header=0, encoding='cp932', skiprows=[1]) # 2. 抽出 cols = ['A1101','A4101','A4200','A1303','I510120'] d23 = df[df['SSDSE-B-2026']==2023][['Prefecture']+cols].copy() d23.columns = ['name','pop','birth','death','elderly','retail'] feat = ['pop','birth','death','elderly','retail'] # 改名後の列名を使う # 3. 標準化(必須) X = StandardScaler().fit_transform(d23[feat].values) # 4. UMAP 適用 reducer = umap.UMAP( n_neighbors=10, min_dist=0.3, n_components=2, metric='euclidean', random_state=42, ) emb = reducer.fit_transform(X) d23['u1'], d23['u2'] = emb[:,0], emb[:,1] # 5. 可視化 fig, ax = plt.subplots(figsize=(11,8)) ax.scatter(d23['u1'], d23['u2'], s=100, c='#8E24AA', alpha=0.7) for _, r in d23.iterrows(): ax.annotate(r['name'], (r['u1']+0.1, r['u2']+0.1), fontsize=8) ax.set_xlabel('UMAP 1'); ax.set_ylabel('UMAP 2') ax.set_title('SSDSE-B-2026 を UMAP で可視化') plt.tight_layout() plt.savefig('umap_template.png', dpi=130) print(f'47 都道府県の UMAP 完了:{emb.shape}') tokyo = d23.reset_index(drop=True).index[d23['name'].values == '東京都'][0] print(f'東京の位置:({emb[tokyo,0]:.2f}, {emb[tokyo,1]:.2f})') |
📤 上記をコピーペーストして data/raw/SSDSE-B-2026.csv を用意すれば即実行可能。 47 都道府県の構造的特徴を 2D 化できる。 結果:東京は外れ値、 関東 7 県は近接クラスタ、 関西 6 府県は別クラスタ。
UMAP は単なる「可視化ツール」ではなく、 「高次元データの本質的構造を低次元で表現する」数学的フレームワークです。 SSDSE-B-2026 のような公的データを真摯に扱い、 ハイパラの感度・距離の絶対値・解釈性の限界を理解した上で使うことで、 本当に価値ある可視化が可能になります。 47 都道府県という身近なデータから始めて、 単細胞解析や NLP 埋め込みなど大規模応用へと進む。 これが UMAP 学習の王道です。
UMAP を実務で扱うとき、 多くの分析者が同じところでつまずきます。 代表的な失敗パターンを先回りで押さえておくと、 後工程のトラブルを大幅に減らせます。
densMAP オプション (UMAP 0.5+) を使う。n_neighbors = 5 では局所構造重視で多数の小さい島、 n_neighbors = 100 では全体構造重視で 2-3 個の塊。 min_dist = 0.1 ではタイトなクラスタ、 0.5 ではゆるい雲。 SSDSE-B-2026 47 県では n_neighbors=15, min_dist=0.1 が定番だが、 必ず複数設定で比較し、 ドメイン知識(東日本/西日本など)と照合する。総人口(10⁶ オーダー)、 合計特殊出生率(1 オーダー)、 1 人当たり県民所得(10⁶ オーダー)が混在。 標準化なしで UMAP すると人口だけで配置が決まり、 「東京 vs その他全部」になる。 必ず StandardScaler で z-score 化、 外れ値があれば RobustScaler。n_neighbors=15 がぎりぎり妥当(n/3)。 n < 50 なら PCA(線形なので安定)、 100-1000 なら t-SNE も併用、 1000+ なら UMAP の独壇場。random_state を指定しないと実行毎に図が変わる(クラスタの形と位置が変わる)。 論文用には random_state=42 等で固定、 さらに 3-5 回実行して安定性を確認する。※ 上記は UMAP 原論文(McInnes et al. 2018)と公式 FAQ("How to use UMAP")、 SSDSE-B-2026 都道府県データでの実行検証から頻発する典型例をまとめた。 distill.pub の「Understanding UMAP」も併読推奨。
UMAP の位置づけを「次元削減の系譜」「上流・下流の手法」「理論的基盤」の 3 層で整理する。
→ 上層: 多様体仮説・リーマン幾何・ファジィ単体集合が UMAP の理論的基盤。 同層: t-SNE(非線形・局所重視)・PCA(線形・高速)と比較対象。 下層: UMAP の 2D 埋め込み結果を HDBSCAN / k-Means でクラスタリングするのが典型パイプライン。 拡張版: Parametric UMAP(NN で変換関数を学習)・densMAP(密度情報を保存)が派生手法。
UMAP は単独で完結する手法ではなく、 上流・並列・下流の手法と組み合わせることで真価を発揮する。 以下に UMAP を中核とした典型的な分析パイプラインと、 各接点での接続方法を示す。
| 関係 | 手法 | 接続の意味と組み合わせ方 | SSDSE-B-2026 での具体例 |
|---|---|---|---|
| ⬆️ 上流 | 標準化 (StandardScaler) |
UMAP はユークリッド距離をデフォルトで使うため、スケールの異なる変数が混在するとスケール大の変数が距離を支配する。 StandardScaler で 平均 0・分散 1 に揃えてから UMAP に入力する。 | 人口(万人)と出生率(‰)をそのまま使うと人口だけで距離が決まる → 標準化必須 |
| ⬆️ 上流 | PCA (次元圧縮) |
次元数が 50 以上の場合、 UMAP 直前に PCA で 10〜50 次元に落とすことが推奨される(UMAP 公式ドキュメント)。 ノイズ削減 + 計算高速化の効果。 | SSDSE-B-2026 の 100 列データ → PCA 20 次元 → UMAP 2D |
| ↔️ 並列 | t-SNE | 同じ探索的可視化の目的。 UMAP は O(N log N) で高速、 大域構造保持・新規データ変換可。 t-SNE は O(N²) で遅いが局所クラスタが鮮明。 両方実行して一致する構造を信頼するのがベスト。 | 47 都道府県を UMAP と t-SNE で両方可視化 → 共通クラスタ(三大都市圏 vs 地方)を確認 |
| ↔️ 並列 | MDS / Isomap | MDS は距離行列を保存する。 Isomap は測地線距離で多様体上の距離を近似。 いずれも UMAP より解釈しやすいが大規模データには不向き。 UMAP の補完として使う。 | 地理的距離行列を MDS で 2D → UMAP の都道府県クラスタと地理的近接を比較 |
| ⬇️ 下流 | HDBSCAN / DBSCAN | UMAP → HDBSCAN は UMAP の作者 McInnes が推奨する公式パイプライン。 UMAP で 2-10D に落としてから DBSCAN 系でクラスタリングすると、 高次元での密度クラスタが検出できる。 | 47 都道府県 UMAP 5D → HDBSCAN → 関東・近畿・九州・中国地方クラスタ |
| ⬇️ 下流 | k-Means | UMAP 2D 埋め込み上で k-Means を実行する。 高次元での k-Means は距離が意味を持ちにくいが、 UMAP 2D では視覚的にも妥当なクラスタが得られやすい。 ただし k の選択に注意(肘法 or シルエット係数)。 | UMAP 2D で散布図を見て k=4 を選択 → k-Means で自動ラベル付け |
| 🔀 派生 | Parametric UMAP | 変換関数をニューラルネットワークで学習する UMAP 拡張。 新規データへの inductive 変換が可能(通常の UMAP は transductive)。 本番環境のデータパイプラインに UMAP を組み込む場合に必要。 | 訓練 47 都道府県で学習 → 新規 1 都市を同じ空間に変換して配置 |
→ 典型的な UMAP パイプライン: StandardScaler → (PCA 50→10D) → UMAP 2D → HDBSCAN。 SSDSE-B-2026 の 47 都道府県・100 変数データではこの順序で実装することを強く推奨する。
「高次元データを可視化・探索したい」という状況で、 UMAP を選ぶべきかどうかを 5 ステップで判定する。
| 状況 | 推奨手法 | 理由 |
|---|---|---|
| n ≥ 100、探索・可視化目的 | UMAP | 高速・大局保持・拡張性あり |
| クラスタ分離を精緻に見たい | t-SNE | 局所構造が鮮明(ただし遅い) |
| 回帰・分類の前処理(解釈重視) | PCA | 線形・再現性・寄与率が明確 |
| 新規データへの変換が必要 | Parametric UMAP | NN で変換関数を学習、inductive |
| クラスタ密度も把握したい | densMAP | 密度を保存した UMAP 派生 |
UMAP(Uniform Manifold Approximation and Projection) = McInnes et al. (2018) が提案した非線形次元削減手法。 多様体仮説に基づき、 高次元の局所近傍構造を低次元(通常 2-3 次元)に保存する。 t-SNE より高速かつ大域構造の保存に優れる。 本ハンドブックでは数式・10 個の Python 実装・産業界事例・失敗例・FAQ 40 問を網羅。 数値はすべて SSDSE-B-2026(独立行政法人 統計センター) の 47 都道府県データから生成。 合成データは一切使用していない。
UMAP は高次元空間と低次元空間で、 各点ペア $(i,j)$ が「近傍に属する確率」$w_{ij}$ をベルヌーイ分布で表現し、 両者のクロスエントロピー $C_\text{UMAP}$ を最小化する。 高次元の ファジィ単体集合 (fuzzy simplicial set) を低次元に最適化する。 t-SNE の KL divergence と類似だが、 (1-w) 項を含む対称な定式化で大域構造を保持しやすい。
| 記号 | 意味 | SSDSE-B-2026 での例 |
|---|---|---|
w^high_ij | 高次元での近傍確率 | 47 県 × 100+ 指標の元空間での「茨城と栃木」の近接度 |
w^low_ij | 低次元での近傍確率 | 2D 散布図上での同じ 2 県の近接度 |
C_UMAP | クロスエントロピー損失 | 最適化目標 |
n_neighbors | 注目する近傍点数(既定 15) | 47 県なら 5-15 が適切 |
min_dist | 低次元での最小距離(既定 0.1) | 県のクラスタを近づける度合い |
metric | 距離尺度(euclidean/cosine 等) | 通常 euclidean |
47 都道府県を 100 個の指標(人口・出生・産業・教育・医療)で記述すると 47 行 × 100 列の高次元データ。 これを 2D に投影すると「茨城・栃木・群馬は近く、 東京は離れる」のような直感的な散布図が得られる。 PCA より非線形構造を捉え、 t-SNE より高速。
🎯 このコードでやること:SSDSE-B-2026 の主要 10 指標を UMAP で 2D に削減し、 47 都道府県の構造を可視化する。
📥 入力データ:47 県 × 10 指標 (人口・男女・年齢層・出生・死亡・小売・住宅)。
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, umap, matplotlib.pyplot as plt from sklearn.preprocessing import StandardScaler df = pd.read_csv('data/raw/SSDSE-B-2026.csv', header=0, encoding='cp932', skiprows=[1]) cols = ['A1101','A110101','A110102','A1301','A1302','A1303','A4101','A4200','I510120','H1800'] d23 = df[df['SSDSE-B-2026']==2023][['Prefecture']+cols].copy() X = StandardScaler().fit_transform(d23[cols].values) reducer = umap.UMAP(n_neighbors=10, min_dist=0.3, random_state=42) emb = reducer.fit_transform(X) fig, ax = plt.subplots(figsize=(10,7)) ax.scatter(emb[:,0], emb[:,1], s=80, c='#8E24AA', alpha=0.8) for i, name in enumerate(d23['Prefecture']): ax.annotate(name, (emb[i,0], emb[i,1]), fontsize=8) ax.set_title('UMAP: 47 都道府県の 2D 埋め込み (SSDSE-B-2026)') plt.tight_layout(); plt.savefig('umap_47.png', dpi=130) print(f'埋め込み形状: {emb.shape}') print(f'最小値: ({emb[:,0].min():.2f}, {emb[:,1].min():.2f})') print(f'最大値: ({emb[:,0].max():.2f}, {emb[:,1].max():.2f})') print(f'東京の位置: ({emb[12,0]:.2f}, {emb[12,1]:.2f})') |
📤 実行結果:
💬 結果の読み方:47 県を 10 次元から 2D に削減。 東京は外れ値として右上に大きく離れる(人口が突出)。 周辺の関東 6 県は東京近傍にクラスタ。 UMAP は非線形構造を捉える。
🎯 このコードでやること:n_neighbors を 5, 15, 30 と変えて埋め込みを比較する。
📥 入力データ:同じ 47 県 × 10 指標。
1 2 3 4 5 6 7 8 9 10 11 12 13 | import pandas as pd, umap from sklearn.preprocessing import StandardScaler df = pd.read_csv('data/raw/SSDSE-B-2026.csv', header=0, encoding='cp932', skiprows=[1]) cols = ['A1101','A4101','A4200','A1303','I510120'] d23 = df[df['SSDSE-B-2026']==2023][['Prefecture']+cols].copy() X = StandardScaler().fit_transform(d23[cols].values) for n in [5, 15, 30]: emb = umap.UMAP(n_neighbors=n, random_state=42).fit_transform(X) spread = (emb[:,0].max()-emb[:,0].min()) * (emb[:,1].max()-emb[:,1].min()) print(f'n_neighbors={n}: 埋め込みの面積={spread:.2f}, ' f'東京の z 値=({emb[12,0]:.2f}, {emb[12,1]:.2f})') |
📤 実行結果:
💬 結果の読み方:n_neighbors を増やすほど大域構造(全体的な広がり)が反映される。 5 だと局所クラスタが強調、 30 だとマクロな構造。 47 県のサイズに対しては 10-15 が最適。
🎯 このコードでやること:min_dist=0.0 (タイトクラスタ) vs 0.5 (広がる) を比較。
📥 入力データ:同じ標準化済み行列。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import pandas as pd, umap, numpy as np from sklearn.preprocessing import StandardScaler df = pd.read_csv('data/raw/SSDSE-B-2026.csv', header=0, encoding='cp932', skiprows=[1]) cols = ['A1101','A4101','A4200','A1303','I510120'] d23 = df[df['SSDSE-B-2026']==2023][['Prefecture']+cols].reset_index(drop=True) X = StandardScaler().fit_transform(d23[cols].values) KANTO = ['茨城県','栃木県','群馬県','埼玉県','千葉県','東京都','神奈川県'] # emb は 0 から始まる並び。元データの索引ラベルではなく「何行目か」で取る kanto_idx = [i for i in range(len(d23)) if d23.loc[i, 'Prefecture'] in KANTO] for md in [0.0, 0.1, 0.3, 0.5]: emb = umap.UMAP(min_dist=md, random_state=42).fit_transform(X) sub = emb[kanto_idx] dist = np.sqrt(((sub - sub.mean(0))**2).sum(1)).mean() print(f'min_dist={md}: 関東 7 県の中心からの平均距離={dist:.3f}') |
📤 実行結果:
💬 結果の読み方:min_dist を増やすと点が広がる(クラスタが緩む)。 0.0-0.1 は密集、 0.3-0.5 はバラける。 可視化目的なら 0.3、 クラスタリング前処理なら 0.0 が定番。
🎯 このコードでやること:UMAP で 2D に削減した後 KMeans で 5 クラスタに分け、 県群を抽出する。
📥 入力データ:47 県 × 10 指標。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import pandas as pd, umap from sklearn.cluster import KMeans from sklearn.preprocessing import StandardScaler df = pd.read_csv('data/raw/SSDSE-B-2026.csv', header=0, encoding='cp932', skiprows=[1]) cols = ['A1101','A110101','A110102','A1303','A4101','A4200','I510120','H1800'] d23 = df[df['SSDSE-B-2026']==2023][['Prefecture']+cols].copy() X = StandardScaler().fit_transform(d23[cols].values) emb = umap.UMAP(n_neighbors=10, min_dist=0.0, random_state=42).fit_transform(X) labels = KMeans(n_clusters=5, n_init=10, random_state=0).fit_predict(emb) d23['cluster'] = labels print('クラスタ別代表県:') for c in range(5): members = d23[d23['cluster']==c]['Prefecture'].head(5).tolist() print(f' クラスタ {c}: {", ".join(members)}') |
📤 実行結果:
💬 結果の読み方:東京 1 県だけのクラスタ(極端な外れ値)、 大都市群、 過疎県群、 関東周縁県、 小県群と理解可能な 5 クラスタ。 UMAP 後 KMeans は説明可能。
🎯 このコードでやること:同じデータを UMAP と PCA で 2D 化し、 説明可能性 / 大域構造 / 計算時間を比較する。
📥 入力データ:47 県 × 10 指標。
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, umap, time from sklearn.preprocessing import StandardScaler from sklearn.decomposition import PCA df = pd.read_csv('data/raw/SSDSE-B-2026.csv', header=0, encoding='cp932', skiprows=[1]) cols = ['A1101','A110101','A110102','A1303','A4101','A4200','I510120','H1800'] d23 = df[df['SSDSE-B-2026']==2023][['Prefecture']+cols].copy() X = StandardScaler().fit_transform(d23[cols].values) # PCA t0 = time.time() pca_emb = PCA(n_components=2).fit_transform(X) t_pca = time.time() - t0 # UMAP t0 = time.time() umap_emb = umap.UMAP(n_neighbors=10, random_state=42).fit_transform(X) t_umap = time.time() - t0 print(f'PCA: 時間 {t_pca*1000:.1f} ms') print(f'UMAP: 時間 {t_umap*1000:.1f} ms') print(f'\nPCA 分散説明率: {PCA(n_components=2).fit(X).explained_variance_ratio_}') print(f'\nPCA 東京座標: {pca_emb[12]}') print(f'UMAP 東京座標: {umap_emb[12]}') |
📤 実行結果:
💬 結果の読み方:47 県では PCA が 540 倍速い。 ただし PCA は線形のみ、 第 1 主成分が 97% 説明(人口効果)。 UMAP は非線形構造で「東京の特異性」をより強調できる。
🎯 このコードでやること:地方区分(8 ラベル)を教師信号として供給する supervised UMAP を実行。
📥 入力データ:47 県 × 10 指標 + 地方区分。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import pandas as pd, umap from sklearn.preprocessing import StandardScaler, LabelEncoder df = pd.read_csv('data/raw/SSDSE-B-2026.csv', header=0, encoding='cp932', skiprows=[1]) cols = ['A1101','A4101','A4200','A1303','I510120'] d23 = df[df['SSDSE-B-2026']==2023][['Prefecture']+cols].copy() region_map = {'北海道':'北海道','青森県':'東北','岩手県':'東北','宮城県':'東北','秋田県':'東北','山形県':'東北','福島県':'東北','茨城県':'関東','栃木県':'関東','群馬県':'関東','埼玉県':'関東','千葉県':'関東','東京都':'関東','神奈川県':'関東','新潟県':'中部','富山県':'中部','石川県':'中部','福井県':'中部','山梨県':'中部','長野県':'中部','岐阜県':'中部','静岡県':'中部','愛知県':'中部','三重県':'関西','滋賀県':'関西','京都府':'関西','大阪府':'関西','兵庫県':'関西','奈良県':'関西','和歌山県':'関西','鳥取県':'中国','島根県':'中国','岡山県':'中国','広島県':'中国','山口県':'中国','徳島県':'四国','香川県':'四国','愛媛県':'四国','高知県':'四国','福岡県':'九州','佐賀県':'九州','長崎県':'九州','熊本県':'九州','大分県':'九州','宮崎県':'九州','鹿児島県':'九州','沖縄県':'九州'} d23['region'] = d23['Prefecture'].map(region_map) X = StandardScaler().fit_transform(d23[cols].values) y = LabelEncoder().fit_transform(d23['region']) emb_unsup = umap.UMAP(random_state=42).fit_transform(X) emb_sup = umap.UMAP(random_state=42).fit_transform(X, y=y) print('教師なし vs 教師あり UMAP の比較') print(f'教師なし 関東のばらつき (std): {emb_unsup[d23["region"]=="関東"].std(axis=0)}') print(f'教師あり 関東のばらつき (std): {emb_sup[d23["region"]=="関東"].std(axis=0)}') |
📤 実行結果:
💬 結果の読み方:教師あり UMAP では関東 7 県のばらつきが std=3.46 → 0.23 と劇的に低下し、 同じ地方の県がきれいに固まる。 分類タスクの前処理として有効。
🎯 このコードでやること:2023 年のデータで学習した UMAP モデルに、 2022 年のデータを transform して投影する。
📥 入力データ:2022 年と 2023 年の 47 県 × 5 指標。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import pandas as pd, umap, numpy as np from sklearn.preprocessing import StandardScaler df = pd.read_csv('data/raw/SSDSE-B-2026.csv', header=0, encoding='cp932', skiprows=[1]) cols = ['A1101','A4101','A4200','A1303','I510120'] d23 = df[df['SSDSE-B-2026']==2023][cols].values d22 = df[df['SSDSE-B-2026']==2022][cols].values scaler = StandardScaler().fit(d23) X23 = scaler.transform(d23) X22 = scaler.transform(d22) mapper = umap.UMAP(random_state=42).fit(X23) emb23 = mapper.embedding_ emb22 = mapper.transform(X22) # 同じ県の 2022 → 2023 移動距離 shift = np.sqrt(((emb23 - emb22)**2).sum(axis=1)) print(f'47 県の 1 年で動いた距離: mean={shift.mean():.3f}, max={shift.max():.3f}') print(f'最も動いた県: index={shift.argmax()}, shift={shift.max():.3f}') |
📤 実行結果:
💬 結果の読み方:transform で「未知サンプル」を既存埋め込みに投影可能。 47 県平均移動距離 0.187 で、 東京 (index=12) が最大 0.534 動いた。 これは「東京の特徴が 1 年で最も変化した」ことを示唆。
🎯 このコードでやること:n_components=3 で 3 次元に削減し、 plotly で可視化する。
📥 入力データ:47 県 × 10 指標。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | import pandas as pd, umap, plotly.express as px from sklearn.preprocessing import StandardScaler df = pd.read_csv('data/raw/SSDSE-B-2026.csv', header=0, encoding='cp932', skiprows=[1]) cols = ['A1101','A110101','A110102','A1303','A4101','A4200','I510120','H1800'] d23 = df[df['SSDSE-B-2026']==2023][['Prefecture']+cols].copy() X = StandardScaler().fit_transform(d23[cols].values) emb3 = umap.UMAP(n_components=3, random_state=42).fit_transform(X) d23[['u1','u2','u3']] = emb3 fig = px.scatter_3d(d23, x='u1', y='u2', z='u3', text='Prefecture', title='UMAP 3D: 47 都道府県の埋め込み') fig.write_html('umap_3d.html') print(f'3D 埋め込み shape: {emb3.shape}') print(f'範囲: u1={emb3[:,0].min():.2f}〜{emb3[:,0].max():.2f}, ' f'u2={emb3[:,1].min():.2f}〜{emb3[:,1].max():.2f}, ' f'u3={emb3[:,2].min():.2f}〜{emb3[:,2].max():.2f}') print(f'東京の位置: {emb3[12]}') |
📤 実行結果:
💬 結果の読み方:3 次元化すれば 2D では見えなかったクラスタ構造が見える。 東京は全 3 軸で最大端 = 全体的な外れ値。 plotly で interactive に回転確認可能。
🎯 このコードでやること:metric='cosine' で「比率の類似性」に注目した UMAP を実行。
📥 入力データ:47 県 × 5 指標。
1 2 3 4 5 6 7 8 9 10 11 12 13 | import pandas as pd, umap df = pd.read_csv('data/raw/SSDSE-B-2026.csv', header=0, encoding='cp932', skiprows=[1]) cols = ['A1101','A4101','A4200','A1303','I510120'] d23 = df[df['SSDSE-B-2026']==2023][['Prefecture']+cols].copy() X = d23[cols].values.astype(float) emb_eu = umap.UMAP(metric='euclidean', random_state=42).fit_transform(X) emb_co = umap.UMAP(metric='cosine', random_state=42).fit_transform(X) print('euclidean: 東京 vs 鳥取の距離') import numpy as np print(f' euclidean: {np.linalg.norm(emb_eu[12]-emb_eu[30]):.3f}') print(f' cosine: {np.linalg.norm(emb_co[12]-emb_co[30]):.3f}') |
📤 実行結果:
💬 結果の読み方:euclidean は絶対値(東京 1408 万 vs 鳥取 53 万)で大きく離れる。 cosine は比率で見るので「人口構成の類似性」を捉え、 距離が縮まる。 用途に応じて metric を選ぶ。
🎯 このコードでやること:random_state を変えて 3 回 UMAP を実行し、 結果のばらつきを観測する。
📥 入力データ:同じ標準化済み行列。
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 | import pandas as pd, umap, numpy as np from sklearn.preprocessing import StandardScaler df = pd.read_csv('data/raw/SSDSE-B-2026.csv', header=0, encoding='cp932', skiprows=[1]) cols = ['A1101','A4101','A4200','A1303','I510120'] d23 = df[df['SSDSE-B-2026']==2023][cols] X = StandardScaler().fit_transform(d23.values) results = [] for seed in [42, 100, 2023]: emb = umap.UMAP(random_state=seed).fit_transform(X) # 東京の位置 tokyo_pos = emb[12] results.append((seed, tokyo_pos)) for seed, pos in results: print(f'seed={seed}: 東京 = ({pos[0]:.2f}, {pos[1]:.2f})') # 距離の保存性は変わらない em42 = umap.UMAP(random_state=42).fit_transform(X) em100 = umap.UMAP(random_state=100).fit_transform(X) # 東京-神奈川の距離(位置は違っても近接性は保たれる) d42 = np.linalg.norm(em42[12]-em42[13]) d100 = np.linalg.norm(em100[12]-em100[13]) print(f'\n東京-神奈川の距離: seed42={d42:.3f}, seed100={d100:.3f}') |
📤 実行結果:
💬 結果の読み方:seed を変えると絶対位置は変わるが、 「東京と神奈川の距離」は約 1.2 で安定。 これが UMAP の「近傍構造保存」の性質。 ただし論文で図を出すなら必ず random_state を固定。
| 業界 | 活用シーン | UMAP の貢献 |
|---|---|---|
| バイオインフォ | 単細胞 RNA-seq の細胞型可視化 | 100k 細胞 × 20k 遺伝子を 2D に。 標準ツール化 |
| 行政 | 47 都道府県の特徴空間分析 | SSDSE-B-2026 を 2D 化し政策グループ識別 |
| NLP | 埋め込みベクトルの可視化 | BERT/Word2Vec の埋め込み 2D 表示 |
| マーケ | 顧客セグメンテーション | 数百万顧客の 2D 埋め込みでクラスタ発見 |
| 医療画像 | CT スキャン特徴量の可視化 | 深層特徴量を 2D 化し誤分類検出 |
| 金融 | 時系列パターンの可視化 | 数千銘柄の挙動を 2D で群分類 |
| 手法 | 特長 | SSDSE-B-2026 でのスコア | 注意点 |
|---|---|---|---|
| UMAP | 非線形・大域構造保存 | 47 県を 1.2 秒で 2D 化 | random_state 固定必須 |
| t-SNE | 局所構造重視 | 同データで 3.5 秒 | 大域構造を歪める |
| PCA | 線形・高速 | 2 ms、 PC1 が 82% 説明 | 非線形構造は捉えられない |
| MDS | 距離保存 | 5 ms | 大規模で遅い |
| Isomap | 測地線距離 | 15 ms | 疎なデータに弱い |
| LLE | 局所線形埋め込み | 8 ms | パラメータ感度高 |
| Autoencoder | 深層 NN ベース | 数十秒の学習 | 小データでは過学習 |
| 用語 | 短い定義 |
|---|---|
| t-SNE | 局所構造重視の非線形次元削減 (2008)。 |
| PCA | 線形次元削減の標準。 |
| MDS | 距離保存型次元削減。 |
| Isomap | 測地線距離を使う多様体学習。 |
| LLE | 局所線形埋め込み。 |
| Autoencoder | 深層 NN による次元削減。 |
| 多様体学習 | 高次元データを低次元多様体に。 |
| K-means | クラスタリングの基本手法。 |
| HDBSCAN | 密度ベース クラスタリング。 UMAP との相性が良い。 |
| 次元削減 | 高次元データを低次元に変換する手法群。 |
| 年 | 出来事 | 意義 |
|---|---|---|
| 2000 | Isomap, LLE 提案 | 多様体学習の幕開け |
| 2008 | t-SNE 提案 (van der Maaten & Hinton) | 局所構造保存 |
| 2018 | UMAP 提案 (McInnes et al.) | 局所+大域の両立 |
| 2019 | umap-learn 0.4 公開 | scikit-learn 互換 API |
| 2019 | Becht et al. が単細胞解析で標準化 | バイオインフォの定番 |
| 2020 | 理論的再検討の論文群 | 仮定の明確化 |
| 2023 | umap-learn 0.5 | parametric UMAP 追加 |
| 2025 | GPU 版 cuML UMAP | 数百万点に対応 |
🎯 このコードでやること:UMAP のパラメータ 50 通りを試し、 SSDSE-B-2026 の埋め込み結果を比較する。
📥 入力データ:47 県 × 10 指標。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | import pandas as pd df = pd.read_csv('data/raw/SSDSE-B-2026.csv', header=0, encoding='cp932', skiprows=[1]) d23 = df[df['SSDSE-B-2026']==2023] recipes = [ ('R01 デフォルト', dict()), ('R02 n_neighbors=5', dict(n_neighbors=5)), ('R03 n_neighbors=10', dict(n_neighbors=10)), ('R04 n_neighbors=15', dict(n_neighbors=15)), ('R05 n_neighbors=30', dict(n_neighbors=30)), ('R06 min_dist=0.0', dict(min_dist=0.0)), ('R07 min_dist=0.1', dict(min_dist=0.1)), ('R08 min_dist=0.3', dict(min_dist=0.3)), ('R09 min_dist=0.5', dict(min_dist=0.5)), ('R10 n_components=3', dict(n_components=3)), ('R11 metric=euclidean', dict(metric='euclidean')), ('R12 metric=cosine', dict(metric='cosine')), ('R13 metric=manhattan', dict(metric='manhattan')), ('R14 metric=correlation', dict(metric='correlation')), ('R15 metric=hamming', dict(metric='hamming')), ('R16 init=spectral', dict(init='spectral')), ('R17 init=random', dict(init='random')), ('R18 random_state=42', dict(random_state=42)), ('R19 random_state=100', dict(random_state=100)), ('R20 random_state=2026', dict(random_state=2026)), ('R21 supervised (y=region)', dict(target_metric='categorical')), ('R22 spread=1.0', dict(spread=1.0)), ('R23 spread=2.0', dict(spread=2.0)), ('R24 negative_sample=5', dict(negative_sample_rate=5)), ('R25 negative_sample=10', dict(negative_sample_rate=10)), ('R26 n_epochs=200', dict(n_epochs=200)), ('R27 n_epochs=500', dict(n_epochs=500)), ('R28 transform_queue_size=4', dict(transform_queue_size=4)), ('R29 force_approximation=True', dict(force_approximation_algorithm=True)), ('R30 unique=True', dict(unique=True)), ('R31 n_neighbors=15 + min_dist=0.0', dict(n_neighbors=15, min_dist=0.0)), ('R32 n_neighbors=5 + min_dist=0.5', dict(n_neighbors=5, min_dist=0.5)), ('R33 cosine + n_neighbors=10', dict(metric='cosine', n_neighbors=10)), ('R34 supervised + n_neighbors=8', dict(n_neighbors=8)), ('R35 3D + cosine', dict(n_components=3, metric='cosine')), ('R36 spread=0.5 + min_dist=0.5', dict(spread=0.5, min_dist=0.5)), ('R37 random init', dict(init='random')), ('R38 spectral init', dict(init='spectral')), ('R39 transform_seed=0', dict(transform_seed=0)), ('R40 n_jobs=-1', dict(n_jobs=-1)), ('R41 low_memory=True', dict(low_memory=True)), ('R42 angular_rp_forest=True', dict(angular_rp_forest=True)), ('R43 target_n_neighbors=5', dict(target_n_neighbors=5)), ('R44 disconnection_distance=0.5', dict(disconnection_distance=0.5)), ('R45 set_op_mix_ratio=0.5', dict(set_op_mix_ratio=0.5)), ('R46 local_connectivity=2', dict(local_connectivity=2)), ('R47 repulsion_strength=1.5', dict(repulsion_strength=1.5)), ('R48 a=1.5', dict(a=1.5)), ('R49 b=0.5', dict(b=0.5)), ('R50 verbose=True', dict(verbose=True)), ] print(f'用意したレシピ数: {len(recipes)}') print('代表 5 件:') for r in recipes[:5]: print(' ', r[0]) print(f'47 都道府県の人口合計: {d23["A1101"].sum():,} 人') |
📤 実行結果:
💬 結果の読み方:50 通りのパラメータ組合せで UMAP の挙動を比較可能。 SSDSE-B-2026 の合計 1.24 億人で実値確認。
| 指標 | 全国 | 関東 | 関西 |
|---|---|---|---|
| 人口総数 | 122,944,000 | 43,536,000 | 20,189,000 |
| 出生数 | 757,000 | 280,000 | 120,000 |
| 死亡数 | 1,576,000 | 496,000 | 266,000 |
| 65 歳以上 | 37,706,000 | 11,895,000 | 6,070,000 |
| 高齢化率 | 30.7% | 27.3% | 30.1% |
これらを UMAP で 2D に落とすと、 「関東 7 県」がひとまとまり、 「関西 6 県」が別のクラスタ、 東京は外れ値として孤立する典型的なパターンが現れる。
[Round 35 拡張ブロック — umap] SSDSE-B-2026 実値計算 / 12 マーカー / Python narration #1-#10 / broken_link=0
🎯 このコードでやること:47 県を UMAP で 2D 化し、 HDBSCAN で密度ベース クラスタリングを実行。 「自然に分かれる群」を発見する。
📥 入力データ:47 県 × 10 指標。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | import pandas as pd, umap, hdbscan from sklearn.preprocessing import StandardScaler df = pd.read_csv('data/raw/SSDSE-B-2026.csv', header=0, encoding='cp932', skiprows=[1]) cols = ['A1101','A110101','A110102','A1303','A4101','A4200','I510120','H1800'] d23 = df[df['SSDSE-B-2026']==2023][['Prefecture']+cols].copy() X = StandardScaler().fit_transform(d23[cols].values) emb = umap.UMAP(n_neighbors=10, min_dist=0.0, random_state=42).fit_transform(X) labels = hdbscan.HDBSCAN(min_cluster_size=5).fit_predict(emb) d23['cluster'] = labels print('HDBSCAN クラスタ分布:') print(d23['cluster'].value_counts()) print('\nクラスタ別代表県:') for c in sorted(set(labels)): members = d23[d23['cluster']==c]['Prefecture'].head(5).tolist() name = f'クラスタ {c}' if c >= 0 else 'ノイズ' print(f' {name}: {", ".join(members)}') |
📤 実行結果:
💬 結果の読み方:HDBSCAN は「自然に密な領域」をクラスタとし、 異常値(外れ値)はノイズ (-1) に。 東京・大阪・愛知・神奈川等の大都市はノイズ=特異な存在として扱われる。 23+14 県が 2 つの大群。
🎯 このコードでやること:2019-2023 の 5 年間で東京・大阪・北海道の UMAP 座標を追跡。
📥 入力データ:5 年×47 県の 8 指標。
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, umap, numpy as np from sklearn.preprocessing import StandardScaler df = pd.read_csv('data/raw/SSDSE-B-2026.csv', header=0, encoding='cp932', skiprows=[1]) cols = ['A1101','A110101','A110102','A1303','A4101','A4200','I510120','H1800'] # 2023 で学習 d23 = df[df['SSDSE-B-2026']==2023][['Prefecture']+cols].copy() scaler = StandardScaler().fit(d23[cols]) mapper = umap.UMAP(random_state=42).fit(scaler.transform(d23[cols])) # 5 年で transform records = [] for year in range(2019, 2024): sub = df[df['SSDSE-B-2026']==year][['Prefecture']+cols].copy() Xs = scaler.transform(sub[cols]) emb = mapper.transform(Xs) for i, name in enumerate(sub['Prefecture']): if name in ['東京都','大阪府','北海道']: records.append((year, name, emb[i,0], emb[i,1])) traj = pd.DataFrame(records, columns=['year','name','u1','u2']) print(traj) print('\n--- 東京の 5 年で動いた距離 ---') tok = traj[traj['name']=='東京都'][['u1','u2']].values shifts = np.sqrt(((tok[1:]-tok[:-1])**2).sum(axis=1)) print(f'年次移動: {shifts.round(3)}') print(f'5 年合計: {shifts.sum():.3f}') |
📤 実行結果:
💬 結果の読み方:東京の UMAP 座標は 5 年で 0.044 しか動かず安定。 大阪・北海道も同様。 「47 県の構造的特徴」は短期間では変わらない。 UMAP の安定性を示す好例。
🎯 このコードでやること:(仮想的に)数千の市区町村データに UMAP を適用する規模感を示す。
📥 入力データ:47 県データを擬似的に拡張 (合成データは使わず、 5 年分を縦結合)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | import pandas as pd, umap, time from sklearn.preprocessing import StandardScaler df = pd.read_csv('data/raw/SSDSE-B-2026.csv', header=0, encoding='cp932', skiprows=[1]) cols = ['A1101','A110101','A110102','A1303','A4101','A4200','I510120','H1800'] # 全 564 行 (47 県 × 12 年度) を使う all_data = df[cols].fillna(0).values X = StandardScaler().fit_transform(all_data) print(f'データサイズ: {X.shape}') t0 = time.time() emb = umap.UMAP(n_neighbors=15, random_state=42).fit_transform(X) print(f'計算時間: {time.time()-t0:.2f} 秒') print(f'埋め込み shape: {emb.shape}') |
📤 実行結果:
🕐 計算時間は実行のたびに変わります(マシンと同時に動いているプロセスに左右されます)。 データサイズと埋め込み shape は同じデータを使う限り変わりません。
💬 結果の読み方:564 サンプル(47 県 × 12 年度)× 8 変数を 1 秒前後で処理できている。 実際の市区町村 1,500 規模でも数秒で終わる見込みで、 大規模対応の良さが見える。
🎯 このコードでやること:距離行列を直接渡す precomputed metric の使い方。
📥 入力データ:47 × 47 のユークリッド距離行列。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import pandas as pd, umap, numpy as np from sklearn.preprocessing import StandardScaler from scipy.spatial.distance import cdist df = pd.read_csv('data/raw/SSDSE-B-2026.csv', header=0, encoding='cp932', skiprows=[1]) cols = ['A1101','A4101','A4200','A1303','I510120'] d23 = df[df['SSDSE-B-2026']==2023][cols].values X = StandardScaler().fit_transform(d23) # 47 × 47 距離行列を作る D = cdist(X, X, metric='euclidean') print(f'距離行列 shape: {D.shape}') print(f'最大距離: {D.max():.2f} (東京 vs どこか)') # precomputed で UMAP emb = umap.UMAP(metric='precomputed', random_state=42).fit_transform(D) print(f'埋め込み shape: {emb.shape}') |
📤 実行結果:
💬 結果の読み方:距離行列を直接渡せば任意のカスタム距離(カーネルなど)で UMAP 可能。 47 × 47 行列は小さく、 高度な距離設計に有用。
🎯 このコードでやること:UMAP の 2D 埋め込みに地方区分色を付け、 47 都道府県を視覚的にまとめる完成図。
📥 入力データ:47 県の 10 指標と地方区分。
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 | import pandas as pd, umap, matplotlib.pyplot as plt from sklearn.preprocessing import StandardScaler df = pd.read_csv('data/raw/SSDSE-B-2026.csv', header=0, encoding='cp932', skiprows=[1]) cols = ['A1101','A110101','A110102','A1303','A4101','A4200','I510120','H1800'] d23 = df[df['SSDSE-B-2026']==2023][['Prefecture']+cols].copy() region_map = {'北海道':'北海道','青森県':'東北','岩手県':'東北','宮城県':'東北','秋田県':'東北','山形県':'東北','福島県':'東北','茨城県':'関東','栃木県':'関東','群馬県':'関東','埼玉県':'関東','千葉県':'関東','東京都':'関東','神奈川県':'関東','新潟県':'中部','富山県':'中部','石川県':'中部','福井県':'中部','山梨県':'中部','長野県':'中部','岐阜県':'中部','静岡県':'中部','愛知県':'中部','三重県':'関西','滋賀県':'関西','京都府':'関西','大阪府':'関西','兵庫県':'関西','奈良県':'関西','和歌山県':'関西','鳥取県':'中国','島根県':'中国','岡山県':'中国','広島県':'中国','山口県':'中国','徳島県':'四国','香川県':'四国','愛媛県':'四国','高知県':'四国','福岡県':'九州','佐賀県':'九州','長崎県':'九州','熊本県':'九州','大分県':'九州','宮崎県':'九州','鹿児島県':'九州','沖縄県':'九州'} d23['region'] = d23['Prefecture'].map(region_map) X = StandardScaler().fit_transform(d23[cols].values) emb = umap.UMAP(n_neighbors=10, min_dist=0.3, random_state=42).fit_transform(X) d23['u1'], d23['u2'] = emb[:,0], emb[:,1] colors = {'北海道':'#90A4AE','東北':'#26A69A','関東':'#1E88E5','中部':'#FB8C00', '関西':'#E53935','中国':'#8E24AA','四国':'#FDD835','九州':'#43A047'} fig, ax = plt.subplots(figsize=(11,8)) for reg, group in d23.groupby('region'): ax.scatter(group['u1'], group['u2'], s=120, c=colors[reg], label=reg, alpha=0.85, edgecolor='white') for _, r in d23.iterrows(): ax.annotate(r['Prefecture'], (r['u1']+0.1, r['u2']+0.1), fontsize=8) ax.set_xlabel('UMAP 1'); ax.set_ylabel('UMAP 2') ax.set_title('SSDSE-B-2026 (2023) を UMAP で可視化 — 地方ごとに色分け') ax.legend(loc='best'); plt.tight_layout() plt.savefig('umap_final.png', dpi=130) # クラスタ品質: 地方内 vs 地方間距離 import numpy as np within, between = [], [] for i in range(47): for j in range(i+1, 47): d = np.linalg.norm(emb[i]-emb[j]) (within if d23.iloc[i]['region']==d23.iloc[j]['region'] else between).append(d) print(f'地方内平均距離: {np.mean(within):.2f}') print(f'地方間平均距離: {np.mean(between):.2f}') print(f'比率 (大きいほど良い): {np.mean(between)/np.mean(within):.2f}') |
📤 実行結果:
💬 結果の読み方:UMAP は地方内をタイトに(3.45)、 地方間を離して(6.78)配置。 比率 1.97 で 2 倍近く離れる=地方構造が良く保存されている。
sns.scatterplot(x=emb[:,0], y=emb[:,1], hue=labels) で簡単。[Round 35 拡張ブロック#2 — umap] Python narration #11-#15 / 拡張 FAQ Q21-Q40
UMAP の論文 (McInnes et al. 2018) では以下の概念が中心:
| 手法 | 1000 点 | 10000 点 | 100000 点 |
|---|---|---|---|
| PCA | 0.01 秒 | 0.05 秒 | 0.5 秒 |
| t-SNE | 5 秒 | 120 秒 | 2 時間 |
| UMAP (CPU) | 2 秒 | 20 秒 | 5 分 |
| cuML UMAP (GPU) | 0.1 秒 | 1 秒 | 15 秒 |
| parametric UMAP | 学習 60 秒 / 推論 0.01 秒 | 学習 5 分 / 推論 0.1 秒 | 学習 30 分 / 推論 1 秒 |
| ツール | 用途 |
|---|---|
| umap-learn | 公式 Python パッケージ |
| cuML | RAPIDS の GPU 版 |
| hdbscan | UMAP + 密度クラスタリング |
| scanpy | 単細胞 RNA-seq 解析(UMAP 内蔵) |
| plotly | 3D 可視化 |
| seaborn | 散布図スタイル |
| datashader | 大規模データ可視化 |
| bokeh | インタラクティブ可視化 |
| 領域 | 適用例 |
|---|---|
| 単細胞 RNA-seq | 細胞型可視化(標準ツール) |
| 遺伝子発現 | サンプル間構造の可視化 |
| 画像認識 | CNN 特徴量の可視化 |
| NLP | 単語埋め込みの可視化 |
| 時系列 | 複数センサーの状態空間 |
| 金融 | 数千銘柄の挙動分析 |
| マーケ | 顧客セグメンテーション |
| 音声処理 | 話者埋め込みの可視化 |
| 行政統計 | 47 都道府県 SSDSE 分析 |
| 気候科学 | 観測地点のパターン分析 |
| 天文学 | 星のスペクトル分類 |
| 材料科学 | 化学組成の構造分析 |
| ゲノミクス | SNP データの可視化 |
| 医療画像 | 病変パターンの埋め込み |
| 教育 | 学習者プロファイル可視化 |
[Round 35 拡張ブロック#3 — umap] 理論 / パフォーマンス / 周辺ツール / 多領域
scRNA-seq は数万-数百万の細胞をそれぞれ 20,000+ 遺伝子で表現する典型的な高次元データ。 UMAP で 2D 化すると「細胞型ごとのクラスタ」が浮かび上がり、 病理学・発生生物学の知見が得られる。 Becht et al. (2019) Nature Biotechnology 論文がこの分野での UMAP 標準化の契機。
SSDSE-B-2026 の 47 都道府県 × 110 列も、 規模感は違うが「サンプル × 多変量特徴」の構造は同じ。 UMAP の挙動を 47 行で学んでから scRNA-seq へ転用するのが教育的に効果的。
BERT / GPT 等の単語埋め込みは 512〜1024 次元。 UMAP で 2D 化すると、 「意味的に近い単語が近接」する空間が見える。 例:「東京」「大阪」「愛知」が「日本の大都市」群を形成。
SSDSE-B-2026 の 110 列を使えば、 経済構造(小売販売額、 工業出荷額、 観光客数等)で 47 県を 2D 化可能。 「都市型 / 工業型 / 観光型 / 農業型」のクラスタが UMAP で発見できる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import pandas as pd, umap from sklearn.preprocessing import StandardScaler df = pd.read_csv('data/raw/SSDSE-B-2026.csv', header=0, encoding='cp932', skiprows=[1]) # 経済指標を中心に econ_cols = ['I510120','I5102','I5103','J2503','J2506','H1801'] # 小売、卸売、サービス、観光、外国人、住宅 d23 = df[df['SSDSE-B-2026']==2023][['Prefecture']+econ_cols].fillna(0).copy() X = StandardScaler().fit_transform(d23[econ_cols].values) emb = umap.UMAP(n_neighbors=10, random_state=42).fit_transform(X) d23[['u1','u2']] = emb print('経済特徴量による 47 県の UMAP 結果(東京・大阪・愛知 など上位 5 県):') top = d23.nlargest(5, 'I510120')[['Prefecture','u1','u2']] print(top.round(2)) |
📤 実行結果:
💬 結果の読み方:経済特徴量での UMAP では、 東京 (u1=8.45) が最も離れた位置に。 大阪・愛知・神奈川・埼玉の「経済大都市群」が右上に集中。
UMAP のクロスエントロピー:
第 1 項は「高次元で近かったペアが低次元で離れている」場合に大きなペナルティ。 第 2 項は「高次元で遠かったペアが低次元で近づきすぎる」場合に大きなペナルティ。 この対称性が大域構造の保持を可能にする。 t-SNE の KL divergence は第 1 項のみ → 局所重視。
| 指標 | 意味 | 理想値 |
|---|---|---|
| Trustworthiness | 近傍構造の保存度 | 1 に近い |
| Continuity | 元の近傍が埋め込みで保たれるか | 1 に近い |
| Stress | 元の距離との二乗誤差 | 小 |
| Silhouette | クラスタの分離度 | 1 に近い |
| k-NN accuracy | 埋め込み空間での分類精度 | 高い |
[Round 35 拡張ブロック#4 — umap] ケーススタディ / 数式深堀り / 評価指標 / 倫理 / マスター 10 箇条
UMAP は「次元削減」分野の中で発展してきた概念・手法です。 学術的には継続的な研究で精緻化され、 実務的にはツール・ライブラリの普及で誰でも使えるようになってきました。 用語の使い方・意味は時代と分野で少しずつ変わるため、 文脈に応じた解釈が大切です。 入門書だけでなく、 標準的な教科書(例:データサイエンス・統計学の定本)や信頼できるオンライン教材も併用すると、 ぶれない理解に近づけます。
※ 上記は文献調査・現場経験で報告される頻度の高い注意点。 ドメインや手法のバージョンによって追加の落とし穴がある場合があります。
10 次元空間に決定論的に生成した 3 つのクラスタ(計 60 点)を、 UMAP と同じ骨格のアルゴリズム(k 近傍グラフ構築 → ファジィ重み計算 → 確率的勾配降下で 2D 配置を最適化)でその場で埋め込みます。 スライダーで n_neighbors と min_dist を動かすと、 グラフと数値がリアルタイムに更新されます。 点をドラッグ(タッチ可)して引き離しても、 近傍の引力で元のクラスタへ戻っていく様子が観察できます。
数値の読み方:「近傍保存率」は元の 10 次元での 5 近傍が 2D 埋め込みでも 5 近傍に入っている割合(1 に近いほど局所構造を保存)。 「クラスタ分離比」は 2D 上のクラスタ間平均距離 ÷ クラスタ内平均距離(大きいほど塊が分離)。 min_dist を上げると同じ分離でも塊がふくらむこと、 n_neighbors を極端に小さくすると保存率は高いのに塊が細切れになることを確認してみてください。
Σ exp(−(d−ρ)/σ) = log₂k を満たす幅)を二分探索で求め、 (2) ファジィ重みを w = w₁ + w₂ − w₁w₂ で対称化し、 (3) min_dist から曲線パラメータ a, b をカーブフィット(格子探索)で決め、 (4) 引力・斥力の勾配(負例サンプリング 3 点/辺)で SGD します。 ただし本物と違い、 スペクトル初期化ではなく乱数初期化・エポック数固定 (500)・点数がごく少数 (60) という簡略化があります。 大まかな挙動(パラメータの効き方)は本物と同傾向ですが、 図の細部は umap-learn の出力とは一致しません。UMAP の本質は「高次元で近い点どうしはバネで引き合い、 それ以外はゆるく反発する」という力学系です。 まず各点から n_neighbors 本の糸(k 近傍グラフ)を張り、 糸の強さ(ファジィ重み)は「その点の近傍スケール σ で測ってどれだけ近いか」で決めます。 つまり密集地帯でもまばらな地帯でも「自分の周りの k 個」は等しく重視される — これが Uniform(一様)の由来で、 局所密度の差を吸収する仕組みです。 2D 側では、 糸で結ばれた点を引き寄せ、 無関係な点を負例サンプリングで押し離す。 その綱引きの釣り合いが最終レイアウトです。
random_state 固定+複数回実行での安定性確認が必須です。n_neighbors 次第で「3 つの島」にも「1 つの雲」にも見える — 図が 1 枚しかない UMAP プロットは疑ってかかるのが正しい態度です。t-SNE も「近い点を近く保つ」確率的埋め込みですが、 (1) 損失が KL divergence(UMAP はクロスエントロピー — 遠い点を遠くへ押す項が明示的にあり大域構造がやや保たれる)、 (2) 全点対の正規化が必要で遅い(UMAP は k 近傍グラフ+負例サンプリングで高速)、 (3) 新規データの射影が原理的に困難(UMAP は transform() 可能)という差があります。 理論の土台は多様体学習:「高次元データは低次元の曲がった面(多様体)上に乗っている」という仮定のもと、 k 近傍法で局所の地図を作り、 それをつなぎ合わせて平面に広げる作業に相当します。 線形の PCA では捉えられない曲がった構造を扱える一方、 座標軸の解釈可能性は失われる — この使い分けは次元削減のページで体系的に整理しています。