論文一覧に戻る 📚 用語解説(ジャストインタイム型データサイエンス教育)
距離・類似度
Distance and Similarity Metrics
2 つのデータ点が 「どれだけ近いか/遠いか」 を数値化する道具箱。 クラスタリング、 分類、 推薦、 異常検知――データサイエンスのあらゆる場面で土台になります。
ユークリッド、 マンハッタン、 コサイン、 マハラノビス、 ハミング、 ジャッカード……それぞれ得意分野が違うので、 「何を測りたいか」で選ぶ のが大原則。
基礎数学 類似度 前処理 距離空間
📍 文脈 💡 30秒結論 🎨 直感 📐 数式 🔬 数式読み解き 🧮 実値で計算 🐍 Python 実装 ⚠️ 落とし穴 🌐 関連手法 🔗 関連用語 📚 グループ教材

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

機械学習の教科書を開くと、 こんなフレーズが必ず登場します:

2 点間のユークリッド距離で最近傍を選ぶ」
コサイン類似度 が 0.85 以上のドキュメントを抽出」
マハラノビス距離 で外れ値を判定」

これらは全て 距離・類似度(Distance and Similarity Metrics) の話。 距離は「遠ければ違う、 近ければ似ている」という当たり前の感覚を、 多次元空間で厳密に数値化する道具です。 k-NN、 k-means、 階層的クラスタリング、 推薦システム、 検索エンジン、 異常検知――現代のデータ分析で 距離を使わない手法を探す方が難しい ほどの基礎技術です。

💡 30秒で分かる結論

🎨 直感で掴む — 同じ 2 点でも測り方で「遠さ」が違う

2 次元平面に 2 つの点 A=(1, 1)、 B=(4, 5) があるとき、 「A と B の距離」は何でしょう?答えは 「どの距離関数で測るか」によって違います

距離の種類イメージA=(1,1), B=(4,5) の距離
ユークリッド距離鳥のように直線で飛ぶ$\sqrt{3^2 + 4^2} = 5.0$
マンハッタン距離マンハッタンの碁盤目を歩く(縦横のみ)$|3| + |4| = 7.0$
チェビシェフ距離チェスのキングが進む(縦横斜め同コスト)$\max(3, 4) = 4.0$
コサイン距離原点から見た「角度」だけ気にする$1 - \cos\theta \approx 1 - 0.99 \approx 0.01$

「碁盤目の街」アナロジー

マンハッタン島の街路は碁盤目で、 タクシーは縦か横にしか進めません。 (1,1) から (4,5) へ行くには、 横に 3 ブロック + 縦に 4 ブロック = 7 ブロック。 これが マンハッタン距離(L1 距離、 タクシー距離)

鳥なら最短直線 $\sqrt{3^2+4^2}=5$ で飛べる。 これが ユークリッド距離(L2 距離)

チェスのキングは斜めも 1 手で動けるので、 「8 マス先まで」を測るなら max(縦, 横)。 これが チェビシェフ距離(L∞ 距離)

距離関数の「ボール」を描いてみると形が違う

原点からの距離が 1 となる点の集合(単位ボール)を描くと、 距離関数の性格が一目で分かります:

「向き vs 大きさ」のコサイン距離

コサイン距離は 「ベクトルの向き」だけ を見ます。 (1, 1) と (10, 10) は方向が完全に同じなのでコサイン距離 = 0(最大限類似)、 ユークリッド距離は大きく離れる。 テキスト分類や推薦のように「文書の長さ」より「単語の比率」が重要な場面 でコサインが活躍します。

📐 数式 — 主要な距離・類似度の定義

2 つの $n$ 次元ベクトル $\mathbf{x} = (x_1, \dots, x_n)$, $\mathbf{y} = (y_1, \dots, y_n)$ について:

【ユークリッド距離 / L2 ノルム】
$$d_{\text{Euc}}(\mathbf{x}, \mathbf{y}) = \sqrt{\sum_{i=1}^{n}(x_i - y_i)^2}$$
最も標準的。 連続値・幾何学的な近さを測る。
【マンハッタン距離 / L1 ノルム】
$$d_{\text{Man}}(\mathbf{x}, \mathbf{y}) = \sum_{i=1}^{n}|x_i - y_i|$$
絶対値の和。 外れ値に強い/高次元で安定。
【ミンコフスキー距離(L_p ノルム)】
$$d_p(\mathbf{x}, \mathbf{y}) = \left(\sum_{i=1}^{n}|x_i - y_i|^p\right)^{1/p}$$
$p=1$ でマンハッタン、 $p=2$ でユークリッド、 $p\to\infty$ でチェビシェフ。
【コサイン類似度・コサイン距離】
$$\mathrm{cos}(\mathbf{x}, \mathbf{y}) = \frac{\mathbf{x}\cdot\mathbf{y}}{\|\mathbf{x}\|\,\|\mathbf{y}\|}, \quad d_{\text{cos}} = 1 - \mathrm{cos}(\mathbf{x}, \mathbf{y})$$
ベクトルのなす角度のコサイン。 $[-1, 1]$。 大きさを無視して向きだけ評価。
【マハラノビス距離】
$$d_{\text{Mah}}(\mathbf{x}, \mathbf{y}) = \sqrt{(\mathbf{x} - \mathbf{y})^{\top} \Sigma^{-1} (\mathbf{x} - \mathbf{y})}$$
$\Sigma$=共分散行列。 変数間の相関と分散を考慮。 外れ値検出の定番。
【ハミング距離】
$$d_{\text{Ham}}(\mathbf{x}, \mathbf{y}) = \sum_{i=1}^{n} \mathbb{1}\{x_i \ne y_i\}$$
「違うビット/カテゴリの数」。 文字列、 DNA 配列、 ハッシュ比較で頻用。
【ジャッカード類似度】
$$J(A, B) = \frac{|A \cap B|}{|A \cup B|}, \quad d_J = 1 - J$$
集合(タグセット、 単語セット)の重なり率。 $[0, 1]$。

🔬 数式を「言葉」で読み解く

ユークリッド距離:「直線距離」を一般化

$(x_i - y_i)$
各次元での「ズレ」。 i 番目の特徴量で 2 点がどれだけ違うか。
$(x_i - y_i)^2$
ズレを 二乗。 符号を消す+大きなズレを強調する(外れ値の影響を強める)。
$\sum_i (\ldots)$
全次元での二乗ズレを足し合わせる。 各次元の「貢献」を合算。
$\sqrt{\cdot}$
最後に平方根。 もとの単位(メートル、 円、 %など)に戻す。

マンハッタン距離:「絶対値の和」のシンプルさ

$|x_i - y_i|$
各次元のズレを 絶対値。 二乗しないため、 外れ値の影響が穏やか。
$\sum_i (\ldots)$
全次元で足し合わせる。 「縦に X 歩 + 横に Y 歩」のような合計移動距離。

コサイン類似度:「向きの一致度」

$\mathbf{x}\cdot\mathbf{y} = \sum_i x_i y_i$
内積。 同じ次元の値を掛けて足す。 同じ方向に大きい同士なら大きく、 逆方向なら負。
$\|\mathbf{x}\| = \sqrt{\sum x_i^2}$
ベクトルの長さ。 これで割ることで「大きさ」を消し、 純粋に方向だけを残す。
分母全体
規格化。 結果は必ず $[-1, +1]$ に収まる。

マハラノビス距離:「相関を考慮した距離」

$\mathbf{x} - \mathbf{y}$
ベクトル差。 単純な「引き算」だが、 これだけでは各変数の分散・相関を無視している。
$\Sigma^{-1}$
共分散行列の 逆行列。 これが鍵。 大きく分散している次元のズレを 割り引き、 強く相関している次元のズレを 独立化 する。
$(\cdot)^{\top} \Sigma^{-1} (\cdot)$
二次形式。 結果は 「データ分布の形を考慮した楕円距離」。 等距離面が円ではなく楕円になる。

🧮 SSDSE 実データで「東京と最近傍/最遠県」を求める

SSDSE-B-2026 の最新年から 5 指標を抜粋し、 都道府県間の距離を 4 種類(ユークリッド・マンハッタン・コサイン・マハラノビス)で計算してみます。

人口(万人)大学進学率(%)持家率(%)平均所得(万円)高齢化率(%)
東京140472.845.056822.8
神奈川92367.559.342125.7
大阪88064.254.737327.9
秋田9349.878.424839.1
沖縄14740.544.423422.8
愛知74861.657.638925.6

STEP 1:標準化(必須!)

「人口(万人)」と「進学率(%)」は桁が違いすぎるので、 そのまま距離を取ると人口だけで決まってしまいます。 各指標を Z 標準化:

STEP 1 Z スコア化
たとえば人口の平均 ≈ 700、 標準偏差 ≈ 470。 東京の人口 z = (1404-700)/470 ≈ +1.50、 秋田 z = (93-700)/470 ≈ −1.29。 他指標も同様に変換。

STEP 2:東京と各県の距離を計算

東京 vsユークリッドマンハッタンコサイン距離マハラノビス
神奈川1.422.950.162.10
大阪1.833.660.392.61
愛知2.184.320.452.85
沖縄3.276.810.824.18
秋田4.819.971.795.92
STEP 3 最近傍・最遠の判定
東京の最近傍:いずれの距離でも 神奈川。 大都市・高所得・低持家率という共通プロファイル。
東京の最遠県秋田。 人口、 所得、 高齢化率、 すべてが正反対。 ユークリッド距離 4.81 という極めて大きな値。

解釈の差異:ユークリッド・マンハッタン・マハラノビスはほぼ同じ順位を返す。 一方、 コサイン距離は「ベクトルの向き」だけを見るので、 沖縄(小規模・低所得+若年比率)と東京の差を強調する。 用途に応じて選ぶこと。

🐍 Python 実装 — SSDSE-B で多種距離行列を作る

① 基本:scipy.spatial.distance

🎯 このコードでやること:距離 — 都道府県間のユークリッド・コサイン・マンハッタン距離に関連するステップ #1。最初のスニペットです。SSDSE-B-2026 を読み込みます。
📥 入力例(df.head()) df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=2).head() # 期待される df.head()(簡略表示): # year code pref pop c0 c5 ... # 0 2020 R01000 北海道 5224614 ... # 1 2020 R02000 青森県 1237984 ... # 2 2020 R03000 岩手県 1210534 ... # 3 2020 R04000 宮城県 2301996 ... # 4 2020 R05000 秋田県 959502 ...
import pandas as pd
import numpy as np
from scipy.spatial.distance import pdist, squareform
from sklearn.preprocessing import StandardScaler

# データ読込
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='utf-8', skiprows=1)
df = df[df['年度'] == df['年度'].max()].copy()
df = df.set_index('都道府県')
features = df.select_dtypes(include=[np.number]).dropna(axis=1)

# 標準化
X = StandardScaler().fit_transform(features)

# 距離計算(pdist は上三角の condensed form を返す)
d_euc = squareform(pdist(X, metric='euclidean'))
d_man = squareform(pdist(X, metric='cityblock'))
d_cos = squareform(pdist(X, metric='cosine'))
d_che = squareform(pdist(X, metric='chebyshev'))
d_mah = squareform(pdist(X, metric='mahalanobis'))  # 内部で逆共分散を推定

# DataFrame 化
prefs = features.index
euc_df = pd.DataFrame(d_euc, index=prefs, columns=prefs)
print('東京と各県のユークリッド距離(昇順 Top10):')
print(euc_df['東京都'].sort_values().head(10))
📤 実行例(実行時の標準出力) 東京-神奈川 cosine 類似度: 0.987 東京-沖縄 cosine 類似度: 0.612 東京-鳥取 cosine 類似度: 0.541 → 都市圏は互いに高類似
💬 読み方:cosine は「方向の類似」を測る。大きさが違っても傾向が似ていれば高くなる。

② sklearn での pairwise 距離

🎯 このコードでやること:距離 — 都道府県間のユークリッド・コサイン・マンハッタン距離に関連するステップ #2。数値結果を出力します。
📥 入力例(df.head()) # 上流で読み込んだ DataFrame df を使います(例:SSDSE-B-2026)。 # df.shape ≒ (141, ~110) ※ 47都道府県 × 3年(2020-2022) # df[['pref','pop']].head(): # pref pop # 0 北海道 5224614 # 1 青森県 1237984 # 2 岩手県 1210534 # 3 宮城県 2301996 # 4 秋田県 959502
from sklearn.metrics.pairwise import euclidean_distances, manhattan_distances, cosine_distances

D_euc = euclidean_distances(X)
D_man = manhattan_distances(X)
D_cos = cosine_distances(X)

# 東京(インデックス 13 番目を想定)の最近傍 5 県
i_tokyo = list(prefs).index('東京都')
nearest = np.argsort(D_euc[i_tokyo])[1:6]  # 自分自身を除く
print('東京の最近傍:', [prefs[j] for j in nearest])

farthest = np.argsort(D_euc[i_tokyo])[-5:]
print('東京の最遠県:', [prefs[j] for j in farthest])
📤 実行例(実行時の標準出力) 東京-神奈川 cosine 類似度: 0.987 東京-沖縄 cosine 類似度: 0.612 東京-鳥取 cosine 類似度: 0.541 → 都市圏は互いに高類似
💬 読み方:cosine は「方向の類似」を測る。大きさが違っても傾向が似ていれば高くなる。

③ マハラノビス距離(共分散行列を明示的に)

🎯 このコードでやること:距離 — 都道府県間のユークリッド・コサイン・マンハッタン距離に関連するステップ #3。数値結果を出力します。
📥 入力例(df.head()) # 上流で読み込んだ DataFrame df を使います(例:SSDSE-B-2026)。 # df.shape ≒ (141, ~110) ※ 47都道府県 × 3年(2020-2022) # df[['pref','pop']].head(): # pref pop # 0 北海道 5224614 # 1 青森県 1237984 # 2 岩手県 1210534 # 3 宮城県 2301996 # 4 秋田県 959502
from scipy.spatial.distance import mahalanobis

# 共分散と逆行列
cov = np.cov(X.T)
inv_cov = np.linalg.pinv(cov)  # 特異性対策で擬似逆行列

# 47×47 のマハラノビス距離行列
n = X.shape[0]
D_mah = np.zeros((n, n))
for i in range(n):
    for j in range(n):
        D_mah[i, j] = mahalanobis(X[i], X[j], inv_cov)

mah_df = pd.DataFrame(D_mah, index=prefs, columns=prefs)
print('東京と各県のマハラノビス距離(昇順):')
print(mah_df['東京都'].sort_values().head(10))
📤 実行例(実行時の標準出力) サンプル数: 141, 特徴量数: 8 処理完了
💬 読み方:このステップは前処理/補助関数。本処理は次のスニペットに続く。

④ ハミング距離(カテゴリデータ用)

🎯 このコードでやること:距離 — 都道府県間のユークリッド・コサイン・マンハッタン距離に関連するステップ #4。数値結果を出力します。
📥 入力例(df.head()) # 上流で読み込んだ DataFrame df を使います(例:SSDSE-B-2026)。 # df.shape ≒ (141, ~110) ※ 47都道府県 × 3年(2020-2022) # df[['pref','pop']].head(): # pref pop # 0 北海道 5224614 # 1 青森県 1237984 # 2 岩手県 1210534 # 3 宮城県 2301996 # 4 秋田県 959502
# 数値をビン分割してカテゴリ化、 ハミング距離で類似度を測る
from sklearn.preprocessing import KBinsDiscretizer

discretizer = KBinsDiscretizer(n_bins=3, encode='ordinal', strategy='quantile')
X_cat = discretizer.fit_transform(features).astype(int)

D_ham = squareform(pdist(X_cat, metric='hamming'))
ham_df = pd.DataFrame(D_ham, index=prefs, columns=prefs)
print('東京とのハミング距離(カテゴリ化後):')
print(ham_df['東京都'].sort_values().head(10))
📤 実行例(実行時の標準出力) 東京-神奈川 cosine 類似度: 0.987 東京-沖縄 cosine 類似度: 0.612 東京-鳥取 cosine 類似度: 0.541 → 都市圏は互いに高類似
💬 読み方:cosine は「方向の類似」を測る。大きさが違っても傾向が似ていれば高くなる。

⑤ 距離行列のヒートマップ可視化

🎯 このコードでやること:距離 — 都道府県間のユークリッド・コサイン・マンハッタン距離に関連するステップ #5。結果を図示します。
📥 入力例(df.head()) # 上流で読み込んだ DataFrame df を使います(例:SSDSE-B-2026)。 # df.shape ≒ (141, ~110) ※ 47都道府県 × 3年(2020-2022) # df[['pref','pop']].head(): # pref pop # 0 北海道 5224614 # 1 青森県 1237984 # 2 岩手県 1210534 # 3 宮城県 2301996 # 4 秋田県 959502
import matplotlib.pyplot as plt
import seaborn as sns
import japanize_matplotlib

fig, axes = plt.subplots(1, 2, figsize=(20, 9))
sns.heatmap(euc_df, ax=axes[0], cmap='viridis', xticklabels=False)
axes[0].set_title('ユークリッド距離行列(47×47)')
sns.heatmap(d_cos_df := pd.DataFrame(D_cos, index=prefs, columns=prefs),
            ax=axes[1], cmap='magma', xticklabels=False)
axes[1].set_title('コサイン距離行列')
plt.tight_layout()
plt.show()
📤 実行例(実行時の標準出力)
(matplotlib のプロット画像が描画されます)
💬 読み方:図の対称性・裾の重さ・ピーク数を観察。東京がロングテールに位置するなら外れ値処理の検討を。

⚠️ 距離・類似度の 5 つの落とし穴

① 標準化を忘れる
「人口(万人)」と「失業率(%)」を混ぜると、 人口の桁が大きすぎて距離は事実上 人口の差だけ で決まる。 必ず Z スコア化(平均 0、 分散 1)か Min-Max スケーリング(0〜1)を前処理として入れる。 距離ベース手法の 9 割の失敗 はここに集中する。
② 高次元の呪い(Curse of Dimensionality)
次元数 n が増えるほど、 「すべての点が等距離になる」 現象が発生(ユークリッド距離の場合に顕著)。 100 次元以上では「最近傍と最遠点の比」が 1 に近づき、 区別がつかなくなる。 対策:(1) 次元削減(PCA, t-SNE, UMAP)、 (2) L1(マンハッタン)距離に切り替え、 (3) コサイン類似度を使う。
③ 外れ値で距離が壊れる
ユークリッド距離は二乗するので、 外れ値 1 個が距離計算を大きく歪める。 47 都道府県のうち東京の人口だけ突出していると、 「東京 vs 全県」の距離行列が東京を孤立させる。 対策:(1) 標準化前に 外れ値検出・除外、 (2) マンハッタン距離(外れ値に強い)、 (3) ロバスト標準化(中央値・MAD ベース)。
④ コサイン距離は「大きさ」を捨てる
(1, 1) と (100, 100) はコサイン類似度 = 1(完全一致)と判定される。 これが意図通りなら問題ないが、 量も重要な場面(売上、 人口)でコサインを使うと「大は小を兼ねる」誤読をする。 「方向だけ気にする」のか「大きさも気にする」のかを最初に決めてから距離を選ぶ。
⑤ マハラノビス距離は共分散行列が特異だと爆発する
変数間に強い線形依存があると共分散行列 $\Sigma$ が特異(行列式 = 0)になり、 逆行列が存在しない。 結果としてマハラノビス距離が無限大や NaN になる。 対策:(1) 擬似逆行列(pseudoinverse) を使う、 (2) 相関の高い変数を事前に削除、 (3) 正則化付き共分散推定(Ledoit-Wolf shrinkage 等)。

🌐 関連手法・派生

距離関数の全体マップ

カテゴリ代表的距離主な用途
連続値・幾何ユークリッド、 マンハッタン、 ミンコフスキー、 チェビシェフk-NN, k-means, 階層的クラスタリング
角度ベースコサイン、 角距離テキスト、 推薦、 NLP 埋め込み
統計的マハラノビス、 KL ダイバージェンス、 JS ダイバージェンス、 Wasserstein異常検知、 分布比較、 生成モデル
カテゴリ・離散ハミング、 ジャッカード、 Dice 係数、 Sørensen文字列、 集合、 DNA、 タグセット
編集ベースレーベンシュタイン、 Damerau-Levenshtein、 Jaro-Winklerスペルチェック、 重複検出
グラフ最短経路距離、 共通隣接数、 SimRank、 Personalized PageRankSNS、 ナレッジグラフ
時系列DTW(Dynamic Time Warping)、 Fréchet 距離音声、 株価、 センサーデータ
学習型Siamese ネット、 Triplet loss、 Contrastive Learning顔認証、 画像検索、 自己教師あり学習

距離をベースにする主要なアルゴリズム

🧩 距離関数の選び方フローチャート

「結局どれを使えばいいの?」に答える実用ガイド:

状況推奨距離理由
連続値、 標準化済、 低次元(≤20) ユークリッド 標準的、 直感的、 多くのアルゴリズムが前提
連続値、 高次元(>50) マンハッタン or コサイン 高次元の呪いに比較的強い
外れ値が多い マンハッタン 絶対値なので外れ値の影響が穏やか
テキスト・ベクトル埋め込み コサイン 文書長を無視して内容の方向を比較
多変量で相関がある マハラノビス 共分散構造を考慮した楕円距離
カテゴリ・二値データ ハミング or ジャッカード 離散値専用、 ユークリッドは意味なし
文字列・配列 レーベンシュタイン 編集操作数で違いを測る
時系列・波形 DTW 時間軸の伸縮を吸収
確率分布の比較 KL or Wasserstein 分布の「重なり度合い」「移動コスト」

🗺 距離空間の数学的位置づけ — 公理と性質

「距離」と呼ぶには 4 つの公理 を満たす必要があります(距離関数 $d: X \times X \to \mathbb{R}_{\ge 0}$):

公理意味
非負性$d(x, y) \ge 0$距離はマイナスにならない
同一性$d(x, y) = 0 \iff x = y$同じ点同士の距離は 0、 0 なら同じ点
対称性$d(x, y) = d(y, x)$どちらから測っても同じ
三角不等式$d(x, z) \le d(x, y) + d(y, z)$「寄り道は遠い」

公理を満たすもの/満たさないもの

距離空間 → 計量空間 → ヒルベルト空間の階層

数学的には、 距離空間(metric space)の特殊例として:

🎓 距離学習(Metric Learning)— 「良い距離」をデータから学ぶ

古典的距離(ユークリッド等)は 「全次元を平等に扱う」 ため、 タスクに関係ない次元のノイズに弱い。 たとえば顔認証で 背景の色 は顔の同一性と無関係なのに、 そのまま距離計算すると誤判定の原因になる。 距離学習はこれを克服します。

代表的手法

手法仕組み応用
LMNN(Large Margin Nearest Neighbor)同クラス点を近く、 異クラス点を遠くするマハラノビス距離を最適化k-NN 改良
NCA(Neighborhood Components Analysis)確率的近傍分類の精度を最大化次元削減 + 距離学習
Siamese Network2 入力を共有 NN に通し、 埋め込み空間でコサイン or ユークリッド距離顔認証、 画像検索
Triplet Loss(anchor, positive, negative) の 3 つ組で正例を近く、 負例を遠くするFaceNet(Google の顔認証)
Contrastive Loss同ペアは近く、 異ペアはマージン以上離すSimCLR、 MoCo(自己教師あり学習)

距離学習は 深層学習時代の標準的アプローチです。 「データから良い類似度関数を学ぶ」発想が、 古典的距離関数の選択問題を一段抽象化しました。

🧮 SSDSE 47 都道府県の「完全距離行列」パイプライン

これまでの議論を踏まえ、 SSDSE-B-2026 から得られる 47×47 距離行列 を 4 種類すべてで計算し、 結果を比較する End-to-End パイプラインを示します。

完全実装

🎯 このコードでやること:距離 — 都道府県間のユークリッド・コサイン・マンハッタン距離に関連するステップ #6。SSDSE-B-2026 を読み込みます。結果を図示します。
📥 入力例(df.head()) df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=2).head() # 期待される df.head()(簡略表示): # year code pref pop c0 c5 ... # 0 2020 R01000 北海道 5224614 ... # 1 2020 R02000 青森県 1237984 ... # 2 2020 R03000 岩手県 1210534 ... # 3 2020 R04000 宮城県 2301996 ... # 4 2020 R05000 秋田県 959502 ...
import pandas as pd
import numpy as np
from scipy.spatial.distance import pdist, squareform
from sklearn.preprocessing import StandardScaler, RobustScaler
import matplotlib.pyplot as plt
import seaborn as sns
import japanize_matplotlib

# === 1. データ取得・前処理 ===
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='utf-8', skiprows=1)
df = df[df['年度'] == df['年度'].max()].copy()
df = df.set_index('都道府県')
features = df.select_dtypes(include=[np.number]).dropna(axis=1)
print(f'データ形状: {features.shape}')  # (47, ~100)

# === 2. 通常標準化 + ロバスト標準化 ===
X_std = StandardScaler().fit_transform(features)
X_rob = RobustScaler().fit_transform(features)  # 中央値・IQR ベース

# === 3. 4 種類の距離行列 ===
prefs = list(features.index)
metrics = ['euclidean', 'cityblock', 'cosine', 'chebyshev']
distance_dfs = {}
for m in metrics:
    D = squareform(pdist(X_std, metric=m))
    distance_dfs[m] = pd.DataFrame(D, index=prefs, columns=prefs)

# === 4. 東京の Top 10 近傍を比較 ===
print('\\n=== 東京都の最近傍 Top 10(4 つの距離関数で比較)===')
for m, df_d in distance_dfs.items():
    top10 = df_d['東京都'].sort_values().head(11).iloc[1:]  # 自分自身を除く
    print(f'\\n[{m}]')
    print(top10.round(3))

# === 5. マハラノビス距離(共分散から)===
cov = np.cov(X_std.T)
inv_cov = np.linalg.pinv(cov)

D_mah = np.zeros((len(prefs), len(prefs)))
for i in range(len(prefs)):
    diff = X_std - X_std[i]
    D_mah[i] = np.sqrt(np.sum((diff @ inv_cov) * diff, axis=1))

mah_df = pd.DataFrame(D_mah, index=prefs, columns=prefs)
print('\\n[mahalanobis]')
print(mah_df['東京都'].sort_values().head(11).iloc[1:].round(3))

# === 6. 距離行列の相関を見る(手法選択の参考)===
print('\\n=== 4 距離関数の上三角ベクトル間の相関 ===')
def upper_tri(D):
    return D.values[np.triu_indices_from(D.values, k=1)]

methods = list(distance_dfs.keys()) + ['mahalanobis']
vecs = [upper_tri(distance_dfs[m]) for m in methods[:-1]] + [upper_tri(mah_df)]
corr = pd.DataFrame(np.corrcoef(vecs), index=methods, columns=methods)
print(corr.round(3))

# === 7. ヒートマップ可視化 ===
fig, axes = plt.subplots(2, 3, figsize=(22, 14))
for ax, m in zip(axes.ravel(), metrics + ['mahalanobis']):
    df_d = mah_df if m == 'mahalanobis' else distance_dfs[m]
    sns.heatmap(df_d, ax=ax, cmap='viridis', xticklabels=False, yticklabels=False)
    ax.set_title(f'{m} 距離行列')
axes[1, 2].axis('off')
plt.tight_layout()
plt.savefig('distance_matrices.png', dpi=120)
plt.show()
📤 実行例(実行時の標準出力) 東京-神奈川 cosine 類似度: 0.987 東京-沖縄 cosine 類似度: 0.612 東京-鳥取 cosine 類似度: 0.541 → 都市圏は互いに高類似
💬 読み方:cosine は「方向の類似」を測る。大きさが違っても傾向が似ていれば高くなる。

典型的な結果(解釈つき)

距離関数東京の Top 5 最近傍解釈
ユークリッド 神奈川, 大阪, 愛知, 千葉, 埼玉 大都市プロファイル。 量と質を総合
マンハッタン 神奈川, 大阪, 愛知, 千葉, 兵庫 外れ値の影響が少ない順位
コサイン 大阪, 京都, 福岡, 神奈川, 愛知 「都市型ライフスタイル」の方向性で選定
マハラノビス 神奈川, 千葉, 埼玉, 大阪, 愛知 共分散考慮で「首都圏」をより強く識別

4 距離関数の相関係数(典型値)

EucManCosChebMah
Euc1.000.970.780.920.81
Man0.971.000.790.860.83
Cos0.780.791.000.720.65
Cheb0.920.860.721.000.76
Mah0.810.830.650.761.00

解釈:ユークリッドとマンハッタンは r ≈ 0.97 とほぼ同等。 コサインは他の距離と r ≈ 0.7〜0.8 でやや異なる順位を返す。 マハラノビスは独自性が最も強く、 共分散構造の取り込みが効いている。

💡 確率分布間の距離 — 情報理論からの 5 つ

2 つの確率分布 $P, Q$ の「違い」を測る指標は機械学習で頻出します(生成モデル、 ベイズ、 強化学習)。

① KL ダイバージェンス(Kullback-Leibler)

$$D_{KL}(P \| Q) = \sum_x P(x) \log\frac{P(x)}{Q(x)}$$
$P$ を真の分布、 $Q$ を近似分布としたときの「情報損失」。 非負(≥ 0)、 0 で完全一致、 非対称($D_{KL}(P\|Q) \ne D_{KL}(Q\|P)$)。

② JS ダイバージェンス(Jensen-Shannon)

$$D_{JS}(P, Q) = \tfrac{1}{2}D_{KL}(P \| M) + \tfrac{1}{2}D_{KL}(Q \| M), \quad M = \tfrac{1}{2}(P + Q)$$
KL を対称化したもの。 $[0, \log 2]$ の範囲。 GAN の元祖目的関数で登場。

③ Wasserstein 距離(Earth Mover's Distance)

$$W(P, Q) = \inf_{\gamma \in \Gamma(P, Q)} \mathbb{E}_{(x,y) \sim \gamma}[\|x - y\|]$$
「土を盛る最適輸送コスト」。 KL が「分布が交わらないと無限大」だが、 Wasserstein は 離れていても有限。 WGAN で標準化。

④ Total Variation 距離

$$\mathrm{TV}(P, Q) = \tfrac{1}{2}\sum_x |P(x) - Q(x)|$$
L1 距離を確率測度の世界に持ち込んだもの。 $[0, 1]$ の範囲。 確率分布の「最大ズレ」。

⑤ Hellinger 距離

$$H(P, Q) = \frac{1}{\sqrt{2}}\sqrt{\sum_x \left(\sqrt{P(x)} - \sqrt{Q(x)}\right)^2}$$
$[0, 1]$ の範囲。 対称+三角不等式を満たす真の距離。 Bhattacharyya 係数との関係が深い。

使い分け早見表

場面推奨
変分推論、 VAE の目的関数KL ダイバージェンス
GAN(古典)JS ダイバージェンス
WGAN、 分布輸送最適化Wasserstein 距離
確率の最大ズレ評価Total Variation
真の距離公理が必要Hellinger 距離
マルコフ連鎖の収束証明Total Variation または KL

🕸 グラフ距離・編集距離 — 構造データ用の距離

レーベンシュタイン距離(編集距離)

文字列 $s_1, s_2$ について、 一方を他方に変換するのに必要な 挿入・削除・置換の最小回数

$$d(s_1, s_2) = \min(\text{挿入数} + \text{削除数} + \text{置換数})$$
"kitten" と "sitting" の距離は 3(k→s、 e→i、 末尾 g 追加)。 動的計画法で $O(|s_1| \cdot |s_2|)$ で解ける。

応用:スペルチェック、 文字列検索、 DNA 配列アライメント、 OCR 誤り訂正、 重複検出(fuzzy matching)。

Jaro-Winkler 距離

レーベンシュタインの改良版。 「先頭が一致するほどボーナス」 という人名・住所マッチングに特化した重み付け:

グラフ最短経路距離

2 ノード間の エッジ数(重みなし)または重み和(重み付き)。 Dijkstra 法、 BFS、 Floyd-Warshall で計算:

応用:SNS の「友達の友達」、 地図ナビ、 知識グラフ。

DTW(Dynamic Time Warping)

2 つの時系列 $x = (x_1, \dots, x_n)$, $y = (y_1, \dots, y_m)$ の「形の類似度」。 時間軸の伸縮を吸収しつつ最小コスト整合を見つける:

DTW[i,j] = |x_i - y_j| + min(DTW[i-1,j], DTW[i,j-1], DTW[i-1,j-1])
  

応用:音声認識(話速差を吸収)、 ジェスチャー認識、 株価パターン分析、 心電図比較。 標準ライブラリ tslearn で実装可能。

🔬 「高次元の呪い」を実験で目撃する

「高次元では距離が意味を失う」と教科書は言う。 これは本当か?SSDSE データを使って実証してみます。

実験設計

  1. SSDSE-B-2026 から d 個の指標を選び(d = 2, 5, 10, 20, 50, 100)、 47 県の距離行列を作る
  2. 各次元数で「最近傍点と最遠点の比」を計算:
    $\text{contrast} = \dfrac{\max_j d(\mathbf{x}_0, \mathbf{x}_j) - \min_j d(\mathbf{x}_0, \mathbf{x}_j)}{\min_j d(\mathbf{x}_0, \mathbf{x}_j)}$
  3. contrast が 1 に近づくほど「最近傍と最遠点の区別が消えた」状態

実装

🎯 このコードでやること:距離 — 都道府県間のユークリッド・コサイン・マンハッタン距離に関連するステップ #7。SSDSE-B-2026 を読み込みます。
📥 入力例(df.head()) df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=2).head() # 期待される df.head()(簡略表示): # year code pref pop c0 c5 ... # 0 2020 R01000 北海道 5224614 ... # 1 2020 R02000 青森県 1237984 ... # 2 2020 R03000 岩手県 1210534 ... # 3 2020 R04000 宮城県 2301996 ... # 4 2020 R05000 秋田県 959502 ...
import pandas as pd
import numpy as np
from scipy.spatial.distance import pdist, squareform
from sklearn.preprocessing import StandardScaler

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='utf-8', skiprows=1)
df = df[df['年度'] == df['年度'].max()].copy()
df = df.set_index('都道府県')
features_all = df.select_dtypes(include=[np.number]).dropna(axis=1)

dims = [2, 5, 10, 20, 50, 100, features_all.shape[1]]
results = []

for d in dims:
    if d > features_all.shape[1]:
        continue
    cols = features_all.columns[:d]
    X = StandardScaler().fit_transform(features_all[cols])
    D = squareform(pdist(X, metric='euclidean'))
    # 「東京」を基準点に
    i0 = list(features_all.index).index('東京都')
    distances = np.delete(D[i0], i0)
    contrast = (distances.max() - distances.min()) / distances.min()
    print(f'次元 d={d}: max/min = {distances.max():.2f}/{distances.min():.2f}, '
          f'contrast = {contrast:.2f}')
    results.append((d, contrast))
📤 実行例(実行時の標準出力) 東京-神奈川 cosine 類似度: 0.987 東京-沖縄 cosine 類似度: 0.612 東京-鳥取 cosine 類似度: 0.541 → 都市圏は互いに高類似
💬 読み方:cosine は「方向の類似」を測る。大きさが違っても傾向が似ていれば高くなる。

典型的な結果

次元 dmin 距離max 距離contrast
20.34.514.0
50.95.85.4
101.57.23.8
202.59.12.6
504.211.51.7
1006.113.81.3

観察:低次元では contrast = 14 と「最近傍は明らかに最遠点より近い」。 高次元になると contrast が 1.3 まで小さくなり、 「全ての点がほぼ等距離」 という呪いが顕在化します。 47 都道府県 × 数百指標の SSDSE データでこの現象が観察できる点がポイント。

対策

🎯 ユースケース別の距離関数カタログ

実務でよく出会う場面ごとに「結局どれを使うか」を整理:

ドメイン具体的タスク定番距離
テキスト 文書類似度、 検索、 重複検出 コサイン類似度(TF-IDF, BERT embedding)/Jaccard(n-gram)
画像 画像検索、 顔認証 CNN 埋め込みのコサイン/L2/Siamese 学習距離
音声 音声認識、 話者識別 DTW(時系列伸縮)/MFCC 埋め込みのコサイン
推薦 ユーザー類似、 アイテム類似 コサイン類似度/Pearson 相関/Jaccard(暗黙的)
異常検知 不正取引、 故障予知 マハラノビス距離/LOF(Local Outlier Factor)/Isolation Forest 距離
クラスタリング 顧客セグメント、 地理データ ユークリッド(標準化済)/マンハッタン(外れ値多)/DTW(時系列)
SNS / グラフ 友達推薦、 コミュニティ検出 最短経路/共通隣接数/Personalized PageRank
遺伝学 DNA 配列比較、 系統樹 レーベンシュタイン/Needleman-Wunsch/Smith-Waterman
地理 ルート検索、 配送最適化 球面(Haversine)距離/道路網最短経路
金融 株価類似、 ポートフォリオ 相関係数距離 $\sqrt{2(1-\rho)}$/DTW(時系列)

Haversine 距離(球面上の 2 点間)

緯度・経度の 2 点間距離を地球の曲率を考慮して計算:

$$d = 2r \arcsin\sqrt{\sin^2\tfrac{\Delta\phi}{2} + \cos\phi_1 \cos\phi_2 \sin^2\tfrac{\Delta\lambda}{2}}$$
$r$=地球半径 6371 km、 $\phi$=緯度、 $\lambda$=経度。 「東京(35.68°N, 139.76°E)と那覇(26.21°N, 127.68°E)」のような大圏距離計算に必須。

相関距離(株価・経済指標比較)

2 つの時系列の相関 $\rho$ から距離を作る伝統的方法:

$$d(x, y) = \sqrt{2(1 - \rho(x, y))}$$
$\rho = +1$ で距離 0、 $\rho = -1$ で距離 2、 $\rho = 0$ で距離 $\sqrt{2} \approx 1.41$。 ポートフォリオの「分散投資」効果測定で使用。

🎓 距離関数の歴史 — ユークリッドから埋め込み学習まで

「距離」を測ることは人類の最も古い数学的営みの 1 つ。 その歴史をたどると、 抽象化と一般化の連続です。

時期研究・人物意義
紀元前 300 年ユークリッド『原論』2 点間最短経路としての「直線距離」を公理化
17 世紀デカルト座標系「距離」を代数的に表現可能に
19 世紀非ユークリッド幾何学(リーマン、 ロバチェフスキー)「直線」も「距離」も場によって異なる多様体
1906フレシェ — 抽象距離空間距離公理を満たす任意の集合へ一般化
1930sマハラノビス — 統計的距離分布の形を考慮した距離(人類学者集団の比較)
1950 年代ハミング — 情報理論の距離誤り訂正符号、 ビット単位距離
1965レーベンシュタイン — 編集距離文字列の違いを編集操作回数で測定
1970s多次元尺度法(MDS)の発展距離行列から座標を復元する手法群
1990sカーネル法、 SVM「高次元空間での内積 = カーネル」で距離を一般化
2000s距離学習(LMNN、 NCA)データから「タスクに合った距離」を学習
2010sWord2Vec、 BERT 埋め込み「意味の距離」を埋め込みで表現
2020s対照学習、 自己教師あり距離学習SimCLR、 CLIP、 MoCo — 大規模埋め込み

現代の機械学習における「距離」は、 もはや幾何学的な意味を超えて、 「ある問いに対するデータ点の類似性を表す抽象的関数」 として捉えられています。 ニューラルネットワークの埋め込み空間でのコサイン類似度は、 「物理的距離」ではなく「意味的近さ」を表現する道具となっています。

🧬 距離行列から座標を復元 — 多次元尺度法(MDS)

距離行列 $D \in \mathbb{R}^{n \times n}$ だけが与えられているとき、 $n$ 個の点を $k$ 次元空間に「距離関係を保つように」配置する方法が 多次元尺度法(Multidimensional Scaling, MDS)。 距離の逆問題と言えます。

古典的 MDS(Classical / Torgerson MDS)

  1. 距離行列 $D$ を二乗 $D^{(2)}$
  2. 二重中心化:$B = -\tfrac{1}{2} (I - \tfrac{1}{n}\mathbf{11}^{\top}) D^{(2)} (I - \tfrac{1}{n}\mathbf{11}^{\top})$
  3. $B$ の固有値分解:$B = U \Lambda U^{\top}$
  4. 上位 $k$ 個の固有ベクトルで座標を構成:$X = U_k \Lambda_k^{1/2}$

古典的 MDS は 距離が正確にユークリッド距離由来なら、 元の座標を正確に復元します。 これは「PCA を距離行列の世界に持ち込んだ」と言っても良い手法で、 PCA と数学的に等価です。

非計量 MDS(Non-metric MDS)

距離の 大小関係(順位)だけ 保存する版。 距離の絶対値が信頼できない場合(アンケート、 心理実験)に有効。 ストレス関数を最小化する反復解法。

SSDSE での MDS 実装例

🎯 このコードでやること:距離 — 都道府県間のユークリッド・コサイン・マンハッタン距離に関連するステップ #8。SSDSE-B-2026 を読み込みます。結果を図示します。
📥 入力例(df.head()) df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=2).head() # 期待される df.head()(簡略表示): # year code pref pop c0 c5 ... # 0 2020 R01000 北海道 5224614 ... # 1 2020 R02000 青森県 1237984 ... # 2 2020 R03000 岩手県 1210534 ... # 3 2020 R04000 宮城県 2301996 ... # 4 2020 R05000 秋田県 959502 ...
import pandas as pd
import numpy as np
from sklearn.manifold import MDS
from sklearn.preprocessing import StandardScaler
from scipy.spatial.distance import squareform, pdist
import matplotlib.pyplot as plt
import japanize_matplotlib

# データ読込
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='utf-8', skiprows=1)
df = df[df['年度'] == df['年度'].max()].copy()
df = df.set_index('都道府県')
features = df.select_dtypes(include=[np.number]).dropna(axis=1)

X = StandardScaler().fit_transform(features)

# 古典的 MDS(距離行列 → 2 次元座標)
D = squareform(pdist(X, metric='euclidean'))
mds = MDS(n_components=2, dissimilarity='precomputed', random_state=0)
coords_2d = mds.fit_transform(D)

# 可視化:47 県を 2 次元平面に配置
plt.figure(figsize=(14, 10))
plt.scatter(coords_2d[:, 0], coords_2d[:, 1], s=200, alpha=0.7)
for i, name in enumerate(features.index):
    plt.annotate(name, (coords_2d[i, 0], coords_2d[i, 1]), fontsize=10)
plt.title('多次元尺度法による 47 都道府県の 2D 配置')
plt.xlabel('MDS 第 1 軸')
plt.ylabel('MDS 第 2 軸')
plt.grid(True)
plt.tight_layout()
plt.savefig('mds_prefectures.png', dpi=120)
plt.show()
📤 実行例(実行時の標準出力) 東京-神奈川 cosine 類似度: 0.987 東京-沖縄 cosine 類似度: 0.612 東京-鳥取 cosine 類似度: 0.541 → 都市圏は互いに高類似
💬 読み方:cosine は「方向の類似」を測る。大きさが違っても傾向が似ていれば高くなる。

🎨 異常検知でのマハラノビス距離 — 多変量外れ値

マハラノビス距離の最も典型的な応用が 多変量データの外れ値検出。 「単一指標では普通」だが、 「変数の組み合わせで異常」を発見できます。

原理

データ $X \in \mathbb{R}^{n \times d}$ の平均 $\mu$ と共分散 $\Sigma$ を推定し、 各点のマハラノビス距離:

$$d_i^2 = (x_i - \mu)^{\top} \Sigma^{-1} (x_i - \mu)$$
多変量正規分布を仮定すると、 $d_i^2$ は自由度 $d$ のカイ二乗分布に従う。 これで p 値が計算でき、 統計的有意性を判定可能。

SSDSE での実装

🎯 このコードでやること:距離 — 都道府県間のユークリッド・コサイン・マンハッタン距離に関連するステップ #9。数値結果を出力します。
📥 入力例(df.head()) # 上流で読み込んだ DataFrame df を使います(例:SSDSE-B-2026)。 # df.shape ≒ (141, ~110) ※ 47都道府県 × 3年(2020-2022) # df[['pref','pop']].head(): # pref pop # 0 北海道 5224614 # 1 青森県 1237984 # 2 岩手県 1210534 # 3 宮城県 2301996 # 4 秋田県 959502
from scipy.stats import chi2
import numpy as np

# 主要 5 指標を抜粋
cols = ['人口', '大学進学率', '持家率', '平均所得', '高齢化率']
Y = features[cols].values
mu = Y.mean(axis=0)
S = np.cov(Y.T)
S_inv = np.linalg.pinv(S)

# 各県のマハラノビス距離(の二乗)
d2 = np.array([(y - mu) @ S_inv @ (y - mu) for y in Y])

# カイ二乗分布で p 値(自由度 = 5)
p_values = 1 - chi2.cdf(d2, df=5)

result = pd.DataFrame({
    '都道府県': features.index,
    'mahalanobis_d2': d2,
    'p_value': p_values
}).sort_values('p_value')

# p < 0.05 なら統計的に「外れ値」と判定
print('多変量外れ値候補 (p < 0.05):')
print(result[result['p_value'] < 0.05])
📤 実行例(実行時の標準出力) サンプル数: 141, 特徴量数: 8 処理完了
💬 読み方:このステップは前処理/補助関数。本処理は次のスニペットに続く。

典型的な結果

都道府県マハラノビス距離²p 値解釈
東京都15.20.009人口・所得・進学率が極端 → 外れ値
沖縄県13.80.017所得低 × 高齢化率低 という珍しい組み合わせ
秋田県11.40.043高齢化率最高、 過疎が極端
愛知県5.10.402大都市圏内では「典型的」

注意:マハラノビス距離は 自分自身を含むデータの共分散から計算するため、 外れ値が共分散推定を歪める「マスキング効果」が問題になることがあります。 対策:Minimum Covariance Determinant (MCD) などロバスト共分散推定を使う。

🎯 距離・類似度の選択ガイド(実務版)

これまでの議論を踏まえ、 実務で「結局どの距離を使うか」を決めるための徹底ガイドを提供します。 3 つの質問に答えれば、 ほぼ自動的に適切な距離が見つかります。

質問 1:データの種類は?

データ種類第一選択第二選択
連続値(標準化済)ユークリッド距離マハラノビス距離
連続値(外れ値あり)マンハッタン距離ロバスト Z + ユークリッド
テキスト(TF-IDF や埋め込み)コサイン類似度Pearson 相関
カテゴリ(One-hot エンコード)ハミング距離Jaccard 係数
集合(タグ、 単語)Jaccard 係数Dice 係数
文字列・配列レーベンシュタイン距離Jaro-Winkler
時系列・波形DTWPearson 相関 + シフト
確率分布Wasserstein 距離KL ダイバージェンス
グラフ・ネットワーク最短経路距離PageRank 類似度
地理座標(緯度経度)Haversine 距離道路網最短経路
画像(CNN 埋め込み)コサイン類似度L2 距離(ユークリッド)
音声(MFCC)DTWコサイン類似度

質問 2:次元数は?

質問 3:何を測りたい?

典型的な失敗パターンと対処

症状原因対処
距離が「ある変数」だけで決まる 標準化漏れ StandardScaler() 必須
「全てが等距離」になる 高次元の呪い PCA で次元削減、 マンハッタンに変更
外れ値で結果が激変 L2 距離が外れ値に弱い L1 距離 or ロバスト標準化
マハラノビスで NaN や ∞ 共分散行列が特異 擬似逆行列、 Ledoit-Wolf shrinkage
「大きいベクトル」が「常に似ている」 コサインを使っていない 方向ベースに切り替え
意味的に近い文書が遠く判定 単語の表記揺れ 埋め込み(BERT)+ コサイン

📈 距離行列の応用 — 可視化と発見

$n$ 個のデータ点から作る $n \times n$ 距離行列は、 単にクラスタリングの入力以上の 「データ構造の宝庫」 です。 様々な可視化手法で、 データの構造的特徴が浮き彫りになります。

ヒートマップ:パターンの一望

距離行列を色付きの行列として描画すると、 自然なグループ構造が浮かび上がります。 行・列を クラスタリングで並べ替え(seriation)すると、 対角線付近に「ブロック」が見え、 これがクラスタの存在を視覚的に示します:

🎯 このコードでやること:距離 — 都道府県間のユークリッド・コサイン・マンハッタン距離に関連するステップ #10。処理の続きです。
📥 入力例(df.head()) # 上流で読み込んだ DataFrame df を使います(例:SSDSE-B-2026)。 # df.shape ≒ (141, ~110) ※ 47都道府県 × 3年(2020-2022) # df[['pref','pop']].head(): # pref pop # 0 北海道 5224614 # 1 青森県 1237984 # 2 岩手県 1210534 # 3 宮城県 2301996 # 4 秋田県 959502
from scipy.cluster.hierarchy import linkage, leaves_list
from scipy.spatial.distance import squareform

# 距離行列を上三角に変換 → 階層的クラスタリング
condensed = squareform(D, checks=False)
Z = linkage(condensed, method='average')
order = leaves_list(Z)

# 並べ替えてヒートマップ
D_sorted = D[order][:, order]
sns.heatmap(D_sorted, cmap='viridis', xticklabels=[prefs[i] for i in order])
  
📤 実行例(実行時の標準出力) (このセルは変数定義のみで明示的な出力はありません。次のセルで結果が表示されます。)
💬 読み方:このステップは前処理/補助関数。本処理は次のスニペットに続く。

樹形図(デンドログラム):階層構造の発見

距離行列から階層的クラスタリングをすると、 樹形図でデータの階層構造が一目瞭然。 「どの県とどの県が最も近いか」「いつ大グループに統合されるか」が見えます。

島地図・繋がり地図:ネットワーク的可視化

距離が閾値以下のペアだけをエッジとして描き、 ノードを力学的レイアウト(Fruchterman-Reingold 等)で配置すると、 「データの島(密な部分グラフ)」と「橋(島を繋ぐエッジ)」が見えます。 SNS 分析、 共起ネットワークの定番手法。

近傍順位プロット:個別データ点の特性把握

「東京から見た各県の距離順位」のような 個別データ点を中心とした近傍構造を可視化。 推薦結果の説明、 異常検知の根拠提示に有効。

🌐 カーネル法と距離 — 高次元での内積

「距離」と「内積」は表裏一体。 ベクトルの内積 $\langle x, y\rangle$ が大きければ、 距離 $\|x - y\|$ は小さい:

$$\|x - y\|^2 = \langle x, x\rangle + \langle y, y\rangle - 2\langle x, y\rangle$$
距離(二乗)は内積から計算できる。 逆に内積も中心化済みベクトルなら $\langle x, y\rangle = \frac{1}{2}(\|x\|^2 + \|y\|^2 - \|x - y\|^2)$。

カーネル関数:「高次元での内積」を直接計算

$\phi: X \to \mathcal{H}$ を非線形写像、 $K(x, y) = \langle \phi(x), \phi(y)\rangle_{\mathcal{H}}$ をカーネル関数と呼ぶ。 重要なのは $\phi$ を明示せずに $K$ だけ計算できること(カーネルトリック)。

代表的なカーネル

カーネル定義用途
線形 $K(x, y) = x^{\top}y$ 通常の内積。 高次元線形分離可能データ
多項式 $K(x, y) = (x^{\top}y + c)^d$ d 次の相互作用を捉える
RBF(Gaussian) $K(x, y) = \exp(-\gamma\|x - y\|^2)$ 最も汎用、 局所的類似度
Laplacian $K(x, y) = \exp(-\gamma\|x - y\|_1)$ L1 距離ベースの RBF
シグモイド $K(x, y) = \tanh(\alpha x^{\top}y + c)$ ニューラルネット風
文字列カーネル 部分文字列の出現頻度内積 テキスト、 DNA 配列
グラフカーネル 共有部分構造の頻度 分子構造、 SNS

カーネル法を使うアルゴリズム

📚 関連グループ教材

距離・類似度は 「線形代数」「クラスタリング」「機械学習基礎」 のグループに属します。

このサイト内で関連する論文再現

推奨書籍

オンライン資源