「似ているものは近くに」を原則に、高次元データを2次元の地図に落とし込むMDS。仕組みと主成分分析との違い、活用例を解説します。
多次元尺度構成法(MDS)とは?原理・種類・Pythonでの実装を解説
多次元尺度構成法(MDS: Multidimensional Scaling)とは、データ間の「距離」や「類似度」の情報をもとに、高次元のデータを低次元(2次元・3次元)の空間に配置して可視化する統計手法です。
似ているデータは近くに、異なるデータは遠くに配置されるため、データの構造や関係性を直感的に把握できます。マーケティング調査、心理学、生態学、テキストマイニングなど幅広い分野で活用されています。
多次元尺度構成法の基本原理
MDSの基本的な考え方は次のとおりです。
- データ間の距離行列(非類似度行列)を入力として受け取る
- 低次元空間(通常2次元)上にデータ点を配置する
- 低次元空間での距離が、元の距離行列をできるだけ再現するように座標を最適化する
最適化の評価指標としてストレス値(Stress)が使われます。ストレス値が小さいほど、元の距離関係を正確に再現できていることを意味します。
ストレス値の目安
| ストレス値 | 適合度 |
|---|---|
| 0.00〜0.05 | 非常に良い(Excellent) |
| 0.05〜0.10 | 良い(Good) |
| 0.10〜0.20 | 許容範囲(Fair) |
| 0.20以上 | 不適切(Poor) |
MDSの種類
1. 計量MDS(Classical MDS / Metric MDS)
距離行列の値そのものを数値データとして扱い、低次元空間での距離が元の距離と一致するよう最適化します。Torgerson法が代表的なアルゴリズムで、主成分分析(PCA)と数学的に等価な結果を得ることもあります。
- 入力:間隔尺度・比率尺度の距離データ
- 用途:物理的距離データ、定量的な類似度データ
2. 非計量MDS(Non-metric MDS)
距離の順序関係(大小関係)のみを保持することを目指します。距離の絶対値ではなく「AとBの距離はCとDの距離より大きい」という順位情報を再現します。
- 入力:順序尺度のデータ(アンケートの主観評価など)
- 用途:心理学実験、ブランドイメージ調査
3. 個人差MDS(INDSCAL)
複数の被験者(回答者)ごとに異なる距離行列がある場合に、共通の空間配置と個人ごとの重みを同時に推定します。
MDSの活用事例
マーケティング:ブランドポジショニングマップ
消費者が感じるブランド間の類似度データをもとに、2次元マップ上にブランドを配置します。自社ブランドと競合ブランドの位置関係を可視化でき、差別化戦略の立案に役立ちます。
自然言語処理:単語の類似度可視化
Word2Vecなどで計算した単語ベクトル間のコサイン距離をMDSで2次元に圧縮し、類似した意味を持つ単語が近くにマッピングされる様子を可視化できます。
生態学:生物群集の類似度分析
異なる調査地点における種の構成データから類似度行列を作成し、MDSで群集構造の違いを視覚的に比較します。
PythonによるMDSの実装
scikit-learnを使えば、数行のコードでMDSを実行できます。
import numpy as np
from sklearn.manifold import MDS
import matplotlib.pyplot as plt
# 距離行列(例:5都市間の距離)
dist_matrix = np.array([
[0, 587, 1212, 701, 1936],
[587, 0, 920, 940, 1745],
[1212, 920, 0, 879, 831],
[701, 940, 879, 0, 1374],
[1936, 1745, 831, 1374, 0]
])
# MDS実行(2次元に配置)
mds = MDS(n_components=2, dissimilarity='precomputed', random_state=42)
coords = mds.fit_transform(dist_matrix)
# 可視化
cities = ['東京', '大阪', '福岡', '名古屋', '札幌']
plt.figure(figsize=(8, 6))
plt.scatter(coords[:, 0], coords[:, 1], s=100)
for i, city in enumerate(cities):
plt.annotate(city, (coords[i, 0]+20, coords[i, 1]+20), fontsize=12)
plt.title('MDSによる都市間距離の2次元マッピング')
plt.xlabel('次元1')
plt.ylabel('次元2')
plt.grid(True, alpha=0.3)
plt.show()
print(f'ストレス値: {mds.stress_:.4f}')
MDSと他の次元削減手法との比較
| 手法 | 入力 | 特徴 | 適したデータ |
|---|---|---|---|
| MDS | 距離行列 | 距離関係を保持して低次元化 | 類似度・距離データ |
| PCA | 特徴量行列 | 分散を最大化する軸を抽出 | 連続値の多変量データ |
| t-SNE | 特徴量行列 | 局所的な構造を保持 | 高次元データのクラスタ可視化 |
| UMAP | 特徴量行列 | 局所+大域構造を保持 | 大規模高次元データ |
MDSの最大の特徴は「距離行列」を直接入力できる点です。元の特徴量データがなくても、ペアワイズの距離・類似度さえあれば分析可能です。
MDSを使う際の注意点
- 次元数の選択:通常は2〜3次元を使用。ストレス値が許容範囲に収まる最小の次元数を選ぶ
- 局所最適解:非計量MDSは最適化の初期値に依存するため、複数回実行して安定した結果を確認する
- データ数の制限:距離行列はO(n²)のメモリを必要とするため、大規模データにはランドマークMDSなどの近似手法を検討する
- 軸の解釈:MDSで得られる軸には固有の意味がないため、回転や反転に注意して結果を解釈する
まとめ
多次元尺度構成法(MDS)は、データ間の距離・類似度を低次元空間に可視化する手法です。計量MDSと非計量MDSの使い分け、ストレス値による適合度評価、PCAやt-SNEとの違いを理解することで、適切な場面で効果的にMDSを活用できます。
Pythonのscikit-learnを使えば簡単に実装でき、マーケティング分析やテキストマイニングなど実務でも役立つ手法です。データの構造を直感的に理解したい場面で、ぜひ活用してみてください。
scikit-learnで多次元尺度構成法(MDS)を実装:距離行列から2D配置を可視化
MDS(多次元尺度構成法)は、高次元データやオブジェクト間の距離情報を低次元空間に配置する手法です。都市間の距離行列から2次元マップを再構成する例で、MDSの仕組みを理解しましょう。
import numpy as np
import matplotlib.pyplot as plt
from sklearn.manifold import MDS
# --- 日本の主要都市間の距離行列(km、概算直線距離)---
cities = ["Tokyo", "Osaka", "Nagoya", "Fukuoka", "Sapporo", "Sendai", "Hiroshima"]
# 対称距離行列(上三角を定義し対称化)
distance_matrix = np.array([
[ 0, 397, 259, 882, 831, 300, 679], # Tokyo
[ 397, 0, 138, 489, 1061, 612, 283], # Osaka
[ 259, 138, 0, 574, 957, 527, 413], # Nagoya
[ 882, 489, 574, 0, 1415, 1101, 262], # Fukuoka
[ 831, 1061, 957, 1415, 0, 531, 1310], # Sapporo
[ 300, 612, 527, 1101, 531, 0, 882], # Sendai
[ 679, 283, 413, 262, 1310, 882, 0], # Hiroshima
])
# --- MDS の実行 ---
mds = MDS(
n_components=2, # 2次元に配置
dissimilarity="precomputed", # 入力が距離行列であることを指定
random_state=42,
normalized_stress="auto"
)
coords = mds.fit_transform(distance_matrix)
print(f"Stress value: {mds.stress_:.2f}")
# --- 可視化 ---
plt.figure(figsize=(10, 8))
plt.scatter(coords[:, 0], coords[:, 1], s=200, c="steelblue", edgecolors="black", zorder=5)
for i, city in enumerate(cities):
plt.annotate(
city,
(coords[i, 0], coords[i, 1]),
textcoords="offset points",
xytext=(10, 10),
fontsize=12,
fontweight="bold"
)
# 都市間の距離をエッジで表示(近い都市ペアのみ)
threshold = 500 # 500km以内の都市ペアを結ぶ
for i in range(len(cities)):
for j in range(i + 1, len(cities)):
if distance_matrix[i, j] < threshold:
plt.plot(
[coords[i, 0], coords[j, 0]],
[coords[i, 1], coords[j, 1]],
"gray", linewidth=0.8, alpha=0.5
)
mid_x = (coords[i, 0] + coords[j, 0]) / 2
mid_y = (coords[i, 1] + coords[j, 1]) / 2
plt.text(mid_x, mid_y, f"{distance_matrix[i,j]}km",
fontsize=8, color="gray", ha="center")
plt.title("MDS: Japanese Cities Reconstructed from Distance Matrix", fontsize=13)
plt.xlabel("Dimension 1")
plt.ylabel("Dimension 2")
plt.grid(True, alpha=0.3)
plt.axis("equal")
plt.tight_layout()
plt.savefig("mds_cities.png", dpi=150)
plt.show()
MDSの出力を見ると、距離行列だけから実際の地理的配置に近いマップが再構成されていることが分かります。dissimilarity="precomputed"は入力がすでに距離行列であることを示し、stress_は元の距離と配置後の距離のずれを数値化した指標です。ストレス値が小さいほど忠実な配置が得られており、一般に0.1以下なら良好とされます。MDSはマーケティングでのブランドポジショニングマップや、生物学での種間類似性の可視化など、距離や類似度のデータを直感的に把握したい場面で活用されます。
