活用テクニック2026年4月4日

多変量分析の完全ガイド — PCAとt-SNEで高次元データの構造を可視化する

数十〜数百の特徴量を持つ高次元データを2次元に圧縮して構造を把握する方法を解説。PCAの寄与率やローディング、t-SNEのperplexity調整、UMAPとの比較まで網羅します。

Qast の多変量分析ダッシュボード。PCA のバイプロットと t-SNE の散布図が表示されている。

現実のデータセットには数十〜数百の特徴量が含まれることが珍しくありません。しかし、人間が直感的に理解できるのは2次元か3次元までです。多変量分析(Multivariate Analysis)は、高次元データの構造を低次元(通常は2次元)に圧縮して可視化する手法群です。この記事では、PCA(主成分分析)と t-SNE を中心に、高次元データの「形」を理解するための手法を解説します。

なぜ次元削減が必要なのか

高次元データをそのまま扱うと「次元の呪い(Curse of Dimensionality)」と呼ばれる問題が発生します。次元が増えるほど、データポイント間の距離が均質化し、最近傍探索やクラスタリングの性能が劣化します。また、特徴量の数がサンプル数を超えると、モデルが過学習しやすくなります。

次元削減には、情報をできるだけ保持しながら特徴量の数を減らすという目的と、高次元データを2Dに射影して構造を可視化するという目的の2つがあります。前者はモデル学習の前処理として、後者は EDA における探索的な可視化として活用されます。Qast の多変量分析は後者に重点を置いています。

PCA(主成分分析)の基本原理

PCA(Principal Component Analysis)は、データの分散が最大になる方向を見つけ、その方向に沿って新しい軸(主成分)を定義する手法です。第1主成分はデータの分散が最大となる方向、第2主成分は第1主成分と直交する方向で分散が最大となる方向、以降も同様に定義されます。元の特徴量空間を主成分空間に変換することで、少数の主成分でデータの大部分の情報を表現できます。

PCA は線形変換であるため、計算が高速で解釈もしやすいのが特徴です。ただし、データに非線形な構造(曲面上に分布しているなど)がある場合は、線形な PCA ではその構造を捉えきれません。

  1. 1

    データの標準化

    PCA の前に各特徴量をスケーリング(平均0、分散1に標準化)します。スケールが異なる特徴量があると、大きなスケールの特徴量が主成分を支配してしまいます。

  2. 2

    共分散行列の固有値分解

    標準化済みデータの共分散行列を計算し、固有値と固有ベクトルを求めます。固有ベクトルが主成分の方向、固有値がその方向の分散の大きさです。

  3. 3

    主成分の選択

    固有値が大きい順に主成分を並べ、累積寄与率が十分(例: 80%以上)になるまでの主成分を選択します。

  4. 4

    データの射影

    選択した主成分の固有ベクトルで構成する変換行列を使い、元のデータを低次元空間に射影します。

python
import numpy as np
import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt

# サンプルデータ(Iris データセット)
from sklearn.datasets import load_iris
iris = load_iris()
X = iris.data
y = iris.target
feature_names = iris.feature_names

# 標準化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# PCA の実行(全主成分を計算)
pca = PCA()
X_pca = pca.fit_transform(X_scaled)

# 寄与率の確認
print("各主成分の寄与率:")
for i, ratio in enumerate(pca.explained_variance_ratio_):
    print(f"  PC{i+1}: {ratio:.4f} ({ratio*100:.1f}%)")
print(f"累積寄与率 (PC1+PC2): {sum(pca.explained_variance_ratio_[:2]):.4f}")

# 2D 散布図
fig, ax = plt.subplots(figsize=(8, 6))
for label in np.unique(y):
    mask = y == label
    ax.scatter(X_pca[mask, 0], X_pca[mask, 1], label=iris.target_names[label], alpha=0.7)
ax.set_xlabel(f"PC1 ({pca.explained_variance_ratio_[0]*100:.1f}%)")
ax.set_ylabel(f"PC2 ({pca.explained_variance_ratio_[1]*100:.1f}%)")
ax.set_title("PCA による 2D 射影")
ax.legend()
plt.tight_layout()
plt.show()

寄与率(Explained Variance Ratio)の読み方

寄与率は、各主成分がデータ全体の分散のうちどれだけを「説明」しているかを示す割合です。第1主成分の寄与率が 72% であれば、「データの変動の 72% は第1主成分の方向で説明できる」ことを意味します。寄与率の棒グラフ(スクリープロット)と累積寄与率のグラフを描くと、「何個の主成分があれば十分か」を判断できます。

一般的な目安として、累積寄与率が 80% を超えるまでの主成分を選択することが多いですが、用途によって基準は異なります。可視化が目的であれば PC1 と PC2 の2次元に固定しますが、モデルの前処理として PCA を使う場合は、交差検証で最適な成分数を決定します。

ローディングプロット — 各特徴量の寄与を読み解く

ローディング(Loadings)は、各主成分における元の特徴量の「重み」を表すベクトルです。ローディングプロットを描くと、各特徴量が主成分空間のどの方向に寄与しているかが分かります。ローディングベクトルの長さが長いほどその特徴量の寄与が大きく、方向が近い特徴量同士は相関が高いことを示します。

バイプロット(Biplot)は、データの散布図とローディングプロットを1つのグラフに重ねたものです。データの分布パターンと特徴量の寄与方向を同時に把握できるため、PCA の解釈で最も有用な可視化の一つです。

python
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.datasets import load_iris

iris = load_iris()
X_scaled = StandardScaler().fit_transform(iris.data)
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)

# バイプロットの描画
fig, ax = plt.subplots(figsize=(9, 7))

# データ点の散布図
for label in np.unique(iris.target):
    mask = iris.target == label
    ax.scatter(X_pca[mask, 0], X_pca[mask, 1],
               label=iris.target_names[label], alpha=0.5, s=30)

# ローディングベクトル(矢印)
loadings = pca.components_.T
scale = 3  # 矢印のスケール
for i, name in enumerate(iris.feature_names):
    ax.arrow(0, 0, loadings[i, 0] * scale, loadings[i, 1] * scale,
             color="red", alpha=0.8, head_width=0.05)
    ax.text(loadings[i, 0] * scale * 1.15, loadings[i, 1] * scale * 1.15,
            name, color="red", fontsize=9)

ax.set_xlabel(f"PC1 ({pca.explained_variance_ratio_[0]*100:.1f}%)")
ax.set_ylabel(f"PC2 ({pca.explained_variance_ratio_[1]*100:.1f}%)")
ax.set_title("バイプロット(Biplot)")
ax.legend()
ax.grid(alpha=0.3)
plt.tight_layout()
plt.show()

バイプロットで矢印の方向が近い特徴量(鋭角を成す)は正の相関、反対方向の特徴量(鈍角〜180度)は負の相関があります。直角に近い特徴量同士は相関が弱いです。これは相関マトリクスの情報を幾何学的に表現したものです。

t-SNE — 非線形な構造を可視化する

t-SNE(t-distributed Stochastic Neighbor Embedding)は、高次元空間でのデータポイント間の「近さ」を低次元空間でも保持するように射影する非線形次元削減手法です。PCA が全体的な分散構造を保つのに対し、t-SNE は「近いデータポイント同士を近くに、遠いデータポイント同士を遠くに」配置することを重視します。

t-SNE の大きな強みは、データ内のクラスター構造(グループの分離)を鮮明に可視化できる点です。PCA では重なって見えるクラスターが、t-SNE では明確に分離されることがあります。ただし、t-SNE は確率的なアルゴリズムであるため、実行するたびに結果が変わり、軸の絶対値には意味がないという特徴があります。

t-SNE の perplexity パラメーター

perplexity は t-SNE の最も重要なハイパーパラメーターで、「各データポイントが何個の近傍点に注目するか」を制御します。perplexity が小さいと局所的な構造に、大きいとグローバルな構造に焦点が当たります。一般的には 5〜50 の範囲で設定し、データセットのサイズに応じて調整します。

perplexity の値によって可視化結果が大きく変わるため、複数の perplexity で実行して結果を比較するのがベストプラクティスです。Qast では perplexity = 30(デフォルト)で実行し、スライダーで値を変更して再計算できます。

python
import numpy as np
import matplotlib.pyplot as plt
from sklearn.manifold import TSNE
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import load_digits

# 手書き数字データセット(64次元 → 2次元)
digits = load_digits()
X_scaled = StandardScaler().fit_transform(digits.data)

# 異なる perplexity で t-SNE を実行
fig, axes = plt.subplots(1, 3, figsize=(18, 5))
perplexities = [5, 30, 50]

for ax, perp in zip(axes, perplexities):
    tsne = TSNE(n_components=2, perplexity=perp, random_state=42, n_iter=1000)
    X_tsne = tsne.fit_transform(X_scaled)

    scatter = ax.scatter(X_tsne[:, 0], X_tsne[:, 1],
                         c=digits.target, cmap="tab10", s=5, alpha=0.7)
    ax.set_title(f"perplexity = {perp}")
    ax.set_xticks([])
    ax.set_yticks([])

plt.colorbar(scatter, ax=axes, label="数字ラベル")
plt.suptitle("perplexity による t-SNE の結果の違い", fontsize=14)
plt.tight_layout()
plt.show()
  1. 1

    perplexity = 5〜10

    局所的な構造(小さなクラスターや密な領域)を強調します。データポイント数が少ない(数百程度)場合に適しています。

  2. 2

    perplexity = 20〜50

    局所構造とグローバル構造のバランスが取れた範囲です。多くのケースでこの範囲が良い結果を返します。

  3. 3

    perplexity > 50

    グローバルな構造を重視しますが、局所的な詳細が失われることがあります。大規模データ(数万件以上)で使用します。

t-SNE の結果を解釈する際の注意点: (1) クラスター間の距離には意味がありません(近くても似ているとは限らない)、(2) クラスターのサイズにも意味がありません(密集度が異なるだけ)、(3) 分離されたクラスターの「中の構造」に注目しましょう。

UMAP との比較 — t-SNE の代替手法

UMAP(Uniform Manifold Approximation and Projection)は、t-SNE と同様に非線形次元削減を行う手法ですが、いくつかの優位点があります。(1) 計算速度が大幅に速い(特に大規模データで顕著)、(2) グローバルな構造(クラスター間の距離関係)をより忠実に保持する、(3) 結果の再現性が高い、の3点です。

一方、UMAP の n_neighbors パラメーターは t-SNE の perplexity と同様に結果に大きく影響するため、パラメーターチューニングは依然として必要です。実務では t-SNE と UMAP の両方を実行し、結果を比較するのがベストプラクティスです。

PCA と t-SNE の使い分け

PCA と t-SNE は補完的な手法であり、それぞれ得意な場面が異なります。PCA は線形な構造の把握、寄与率による解釈、高速な計算が強みです。t-SNE は非線形な構造の可視化、クラスターの分離が強みです。両方を併用することで、データの構造をより多角的に理解できます。

  1. 1

    PCA を選ぶ場面

    データの主要な変動方向を理解したい場合、各特徴量の寄与度を知りたい場合、モデルの前処理として次元削減する場合。結果が決定的(毎回同じ)で解釈しやすいのも利点です。

  2. 2

    t-SNE を選ぶ場面

    データにクラスター構造があるか探索したい場合、非線形な構造(マニフォールド)を可視化したい場合。PCA では見えなかったグループ分けが浮かび上がることがあります。

  3. 3

    両方使う場面(推奨)

    EDA では PCA と t-SNE の両方を実行して比較するのが最も確実です。Qast ではどちらも自動実行され、タブで切り替えながら比較できます。

2D 射影の解釈で気をつけること

次元削減による2D射影を解釈する際には、いくつかの重要な注意点があります。まず、2D射影は高次元空間の「影」に過ぎず、必ず情報の損失が伴います。PCA の場合は累積寄与率で損失の程度を評価できますが、t-SNE にはそのような定量指標がありません。

また、2D上で近くにあるデータポイントが高次元空間でも本当に近いとは限りません。逆に、2D上で離れているポイントが高次元では近い場合もあります(特に t-SNE で顕著)。射影結果はあくまで「探索のヒント」として活用し、重要な知見は元の高次元データに戻って検証することが大切です。

Qast の多変量分析ダッシュボード

Qast の EDA では、PCA と t-SNE の両方が自動実行され、統合的なダッシュボードで結果を閲覧できます。PCA セクションには、2D 散布図、スクリープロット(寄与率棒グラフ)、累積寄与率グラフ、ローディングプロット、バイプロットが含まれます。t-SNE セクションには、2D 散布図と perplexity のスライダーが用意されています。

散布図にはターゲット変数やカテゴリ変数で色分けする機能があり、「クラスターとターゲットのクラスがどの程度一致しているか」を視覚的に確認できます。クラスターごとにターゲットの分布が異なる場合は、そのクラスター構造が予測に有用な情報を持っている可能性があります。

まとめ — 高次元データの可視化ワークフロー

多変量分析は高次元データの「形」を理解するための必須ツールです。PCA で線形な構造と各特徴量の寄与を把握し、t-SNE で非線形なクラスター構造を探索するのが基本ワークフローです。両者の結果を比較することで、データの構造をより確信を持って理解できます。Qast はこのワークフローを自動化し、プログラミング不要で高次元データの可視化・探索を実現します。

多変量分析の前に、特徴量関連性分析で冗長な特徴量を整理しておくと、より鮮明な射影結果が得られます。また、多変量分析で発見したクラスター構造は、次のクラスタリング分析でより詳細に分析できます。Qast ではこれらの分析がシームレスに連携しています。

Qast を導入してみませんか?

導入のご相談やデモのご依頼は、お気軽にお問い合わせください。