正準相関分析(CCA)とは?2つのデータ群の関係を図解でわかりやすく解説

ある学校で、生徒の「学力テスト」の結果(数学・英語・理科の3科目)と「身体測定」の結果(身長・体重・握力の3項目)を同時に取得したとします。学力と体力にはどのような関係があるのでしょうか。各科目と各身体測定の間の相関を個別に見ることはできますが、それでは $3 \times 3 = 9$ 個の相関係数を見ることになり、全体像が把握しにくくなります。「学力全体」と「体力全体」の間の関係を、最も強い方向から順に捉えることはできないのでしょうか。

この問いに答えるのが正準相関分析(CCA: Canonical Correlation Analysis)です。CCAは、2組の変数群の間の関係を、正準相関と呼ばれる最大化された相関係数を通じて分析する多変量統計手法です。

CCAを理解すると、以下のような応用が開けます。

  • 神経科学: 脳の活動パターン(fMRIデータ)と行動データの間の関係の分析
  • 自然言語処理: 2つの言語の単語表現の間の対応付け(多言語埋め込み)
  • リモートセンシング: 衛星の異なるセンサーバンドのデータ間の関係の分析
  • 経済学: マクロ経済変数群と金融市場変数群の間の関係の定量化

本記事では、CCAの理論を一般化固有値問題として定式化し、導出の全過程を示します。PythonでCCAを実装し、合成データで動作を確認します。

正準相関分析の概念図。2つのデータ群をそれぞれ合成変数にまとめ相関を最大化する

この図がCCAの全体像です。学力(数学・英語・理科)の群Xと、体力(身長・体重・握力)の群Yを、それぞれ1本の合成変数 $u = \bm{a}^\top\bm{x}$、$v = \bm{b}^\top\bm{y}$ にまとめ、その相関 $\rho = \mathrm{Corr}(u,v)$ が最大になるように重み $\bm{a}, \bm{b}$ を選ぶ——これがCCAの核心です。以下でこの直感を数式に落とし込んでいきます。

本記事の内容

  • 正準相関分析の直感的な理解
  • 正準変量と正準相関の数学的定式化
  • ラグランジュ乗数法による導出と一般化固有値問題
  • 正準変量の性質(直交性と無相関性)
  • Pythonでの実装と可視化

前提知識

この記事を読む前に、以下の記事を読んでおくと理解が深まります。

正準相関分析の直感的な理解

2組のデータ間の「最適な関係」を探す

通常の相関係数は2つのスカラー変数の間の線形関係の強さを測る指標です。しかし、2つの変数(それぞれ複数の変数を含む)の間の関係を一つの数値で要約したいとき、どうすればよいでしょうか。

ここで自然に浮かぶアイデアは、各変数群の線形結合を考えることです。変数群 $\bm{x}$ から $u = \bm{a}^\top\bm{x}$、変数群 $\bm{y}$ から $v = \bm{b}^\top\bm{y}$ という合成変数を作り、$u$ と $v$ の相関が最大になるような重み $\bm{a}$ と $\bm{b}$ を見つけるのです。

これはPCAのアイデアと似ていますが、PCAが「1つのデータ群の中で分散を最大化する方向」を探すのに対し、CCAは「2つのデータ群の間で相関を最大化する方向の組」を探します。

最大化された相関を第1正準相関、対応する線形結合 $u$ と $v$ を第1正準変量(canonical variates)と呼びます。第1正準変量と無相関な制約のもとで次に相関が大きい方向を見つけると、第2正準相関・第2正準変量が得られます。

この構造は、PCAの主成分と類似していますが、1つのデータ群の中の分散ではなく、2つのデータ群の間の相関を扱う点が本質的に異なります。

PCAは1つの群の分散を最大化、CCAは2つの群の間の相関を最大化することの対比図

左のPCAは1つのデータ群の中で分散が最大になる方向を探すのに対し、右のCCAは2つの群それぞれから作った合成変数 $u_1, v_1$ の相関が最大になる方向を探します。右図では点が対角線にきれいに乗り、群Xと群Yの合成が強く相関している($\rho_1 \approx 0.98$)ことが見て取れます。「群内の分散」か「群間の相関」か——ここが両手法の分かれ道です。

では、この直感を数学的に定式化しましょう。

数学的定式化

問題設定

$n$ 個の観測データにおいて、2組の変数群を考えます。

  • $\bm{x} \in \mathbb{R}^p$: 第1変数群($p$ 個の変数)
  • $\bm{y} \in \mathbb{R}^q$: 第2変数群($q$ 個の変数)

$p \leq q$ と仮定します(そうでない場合は $\bm{x}$ と $\bm{y}$ を入れ替える)。すべての変数は中心化されている(平均ゼロ)とします。

2組の変数群をまとめた結合ベクトル $\bm{z} = (\bm{x}^\top, \bm{y}^\top)^\top$ の共分散行列を次のようにブロック分割します。

$$ \begin{equation} \bm{\Sigma} = \text{Cov}\begin{pmatrix}\bm{x}\\\bm{y}\end{pmatrix} = \begin{pmatrix}\bm{\Sigma}_{xx} & \bm{\Sigma}_{xy}\\\bm{\Sigma}_{yx} & \bm{\Sigma}_{yy}\end{pmatrix} \end{equation} $$

ここで $\bm{\Sigma}_{xx} = \text{Cov}(\bm{x})$ は $p \times p$、$\bm{\Sigma}_{yy} = \text{Cov}(\bm{y})$ は $q \times q$、$\bm{\Sigma}_{xy} = \text{Cov}(\bm{x}, \bm{y})$ は $p \times q$ の行列です。$\bm{\Sigma}_{yx} = \bm{\Sigma}_{xy}^\top$ です。

結合共分散行列を4つのブロックΣxx・Σxy・Σyx・Σyyに分割した図

図のように、結合共分散行列は4つのブロックに分かれます。対角の $\bm{\Sigma}_{xx}, \bm{\Sigma}_{yy}$ は各群の内部構造(分散共分散)、非対角の $\bm{\Sigma}_{xy}$ が群と群のつながり(相互共分散)です。CCAの主役はこの $\bm{\Sigma}_{xy}$ で、対角ブロックは後述の白色化に使われます。

最適化問題

正準変量 $u = \bm{a}^\top\bm{x}$ と $v = \bm{b}^\top\bm{y}$ の相関係数は次のように書けます。

$$ \rho = \text{Corr}(u, v) = \frac{\text{Cov}(u, v)}{\sqrt{\text{Var}(u)}\sqrt{\text{Var}(v)}} $$

各項を計算すると次のようになります。

$$ \text{Cov}(u, v) = \text{Cov}(\bm{a}^\top\bm{x}, \bm{b}^\top\bm{y}) = \bm{a}^\top\bm{\Sigma}_{xy}\bm{b} $$

$$ \text{Var}(u) = \text{Var}(\bm{a}^\top\bm{x}) = \bm{a}^\top\bm{\Sigma}_{xx}\bm{a} $$

$$ \text{Var}(v) = \text{Var}(\bm{b}^\top\bm{y}) = \bm{b}^\top\bm{\Sigma}_{yy}\bm{b} $$

CCAの第1正準相関は、この相関係数を最大化する問題です。

$$ \begin{equation} \rho_1 = \max_{\bm{a}, \bm{b}} \frac{\bm{a}^\top\bm{\Sigma}_{xy}\bm{b}}{\sqrt{\bm{a}^\top\bm{\Sigma}_{xx}\bm{a}}\sqrt{\bm{b}^\top\bm{\Sigma}_{yy}\bm{b}}} \end{equation} $$

相関係数はスケール不変($\bm{a}$ や $\bm{b}$ を定数倍しても変わらない)なので、分散を1に制約して分子だけを最大化する形に書き換えられます。

$$ \begin{equation} \max_{\bm{a}, \bm{b}} \quad \bm{a}^\top\bm{\Sigma}_{xy}\bm{b} \quad \text{subject to} \quad \bm{a}^\top\bm{\Sigma}_{xx}\bm{a} = 1, \quad \bm{b}^\top\bm{\Sigma}_{yy}\bm{b} = 1 \end{equation} $$

この定式化からラグランジュ乗数法で解を導出します。

単位分散の制約(円)の上で群間相関が最大になる方向を選ぶ最適化の幾何図

図は最適化のイメージです。制約 $\bm{a}^\top\bm{\Sigma}_{xx}\bm{a}=1$(分散を1に固定)は $\bm{a}$ を単位の楕円(白色化後は円)上に制限します。その円周上のさまざまな向きのうち、群Yとの相関が最大になる1つの向きが第1正準変量の重み $\bm{a}_1$ です。分散を固定して分子(共分散)だけを最大化する、という制約付き最適化になっているのがポイントです。

ラグランジュ乗数法による導出

ラグランジュ関数の構成

2つの等式制約を持つ最大化問題なので、2つのラグランジュ乗数 $\lambda$ と $\mu$ を導入します。

$$ \begin{equation} \mathcal{L}(\bm{a}, \bm{b}, \lambda, \mu) = \bm{a}^\top\bm{\Sigma}_{xy}\bm{b} – \frac{\lambda}{2}(\bm{a}^\top\bm{\Sigma}_{xx}\bm{a} – 1) – \frac{\mu}{2}(\bm{b}^\top\bm{\Sigma}_{yy}\bm{b} – 1) \end{equation} $$

$\bm{a}$ と $\bm{b}$ に関する停留条件を求めます。

停留条件

$\bm{a}$ に関する微分をゼロとおくと次のようになります。

$$ \frac{\partial \mathcal{L}}{\partial \bm{a}} = \bm{\Sigma}_{xy}\bm{b} – \lambda\bm{\Sigma}_{xx}\bm{a} = \bm{0} $$

$\bm{b}$ に関する微分をゼロとおくと次のようになります。

$$ \frac{\partial \mathcal{L}}{\partial \bm{b}} = \bm{\Sigma}_{yx}\bm{a} – \mu\bm{\Sigma}_{yy}\bm{b} = \bm{0} $$

これらを整理すると、連立方程式が得られます。

$$ \begin{equation} \bm{\Sigma}_{xy}\bm{b} = \lambda\bm{\Sigma}_{xx}\bm{a} \end{equation} $$

$$ \begin{equation} \bm{\Sigma}_{yx}\bm{a} = \mu\bm{\Sigma}_{yy}\bm{b} \end{equation} $$

ラグランジュ乗数の関係

第1式の両辺に左から $\bm{a}^\top$ を掛けると $\bm{a}^\top\bm{\Sigma}_{xy}\bm{b} = \lambda\bm{a}^\top\bm{\Sigma}_{xx}\bm{a} = \lambda$ が得られます(制約 $\bm{a}^\top\bm{\Sigma}_{xx}\bm{a} = 1$ を使用)。

同様に、第2式の両辺に左から $\bm{b}^\top$ を掛けると $\bm{b}^\top\bm{\Sigma}_{yx}\bm{a} = \mu\bm{b}^\top\bm{\Sigma}_{yy}\bm{b} = \mu$ が得られます。

$\bm{a}^\top\bm{\Sigma}_{xy}\bm{b} = (\bm{b}^\top\bm{\Sigma}_{yx}\bm{a})^\top = \bm{b}^\top\bm{\Sigma}_{yx}\bm{a}$(スカラーなので転置しても同じ)より、$\lambda = \mu$ です。

さらに、$\lambda = \bm{a}^\top\bm{\Sigma}_{xy}\bm{b} = \text{Cov}(u, v) = \rho$(制約条件のもとで相関と共分散は等しい)です。つまり、ラグランジュ乗数は正準相関そのものです。

一般化固有値問題への変換

連立方程式を $\bm{a}$ だけの方程式に帰着させましょう。第2式から $\bm{b}$ を解くと次のようになります。

$$ \bm{b} = \frac{1}{\rho}\bm{\Sigma}_{yy}^{-1}\bm{\Sigma}_{yx}\bm{a} $$

これを第1式に代入します。

$$ \bm{\Sigma}_{xy}\left(\frac{1}{\rho}\bm{\Sigma}_{yy}^{-1}\bm{\Sigma}_{yx}\bm{a}\right) = \rho\bm{\Sigma}_{xx}\bm{a} $$

整理すると次の式になります。

$$ \bm{\Sigma}_{xy}\bm{\Sigma}_{yy}^{-1}\bm{\Sigma}_{yx}\bm{a} = \rho^2\bm{\Sigma}_{xx}\bm{a} $$

さらに両辺に左から $\bm{\Sigma}_{xx}^{-1}$ を掛けると次の固有値問題が得られます。

$$ \begin{equation} \bm{\Sigma}_{xx}^{-1}\bm{\Sigma}_{xy}\bm{\Sigma}_{yy}^{-1}\bm{\Sigma}_{yx}\bm{a} = \rho^2\bm{a} \end{equation} $$

これは行列 $\bm{M}_1 = \bm{\Sigma}_{xx}^{-1}\bm{\Sigma}_{xy}\bm{\Sigma}_{yy}^{-1}\bm{\Sigma}_{yx}$ の固有値問題です。固有値 $\rho^2$ の正の平方根が正準相関であり、固有ベクトルが正準変量の重みベクトル $\bm{a}$ です。

同様に $\bm{b}$ に関しても、$\bm{M}_2 = \bm{\Sigma}_{yy}^{-1}\bm{\Sigma}_{yx}\bm{\Sigma}_{xx}^{-1}\bm{\Sigma}_{xy}$ の固有値問題に帰着します。$\bm{M}_1$ と $\bm{M}_2$ は同じ非ゼロ固有値を持ちます。

正準相関の数は $\min(p, q)$ 個であり、$\rho_1 \geq \rho_2 \geq \dots \geq \rho_{\min(p,q)} \geq 0$ の順に並べます。

白色化して相互共分散をSVDすると特異値が正準相関になるという解法の流れ図

この固有値問題は、実は次の3ステップと等価です。①各群を白色化(無相関・分散1)し、②白色化した空間で相互共分散行列 $K = \bm{\Sigma}_{xx}^{-1/2}\bm{\Sigma}_{xy}\bm{\Sigma}_{yy}^{-1/2}$ を作り、③これを特異値分解(SVD)する。すると 特異値がそのまま正準相関 $\rho_k$、特異ベクトルが正準変量の方向になります。「白色化してからSVD」という見方は、PCA(1群の共分散をSVD)とCCA(2群の相互共分散をSVD)の対応をきれいに示してくれます。

こうしてCCAの核心的な結果が得られました。次に、正準変量のいくつかの重要な性質を確認しましょう。

正準変量の性質

正準変量間の無相関性

CCAの結果として得られる正準変量には、以下の直交性・無相関性の性質があります。

同じ変数群内の正準変量の無相関性:

$$ \begin{equation} \text{Cov}(u_i, u_j) = \bm{a}_i^\top\bm{\Sigma}_{xx}\bm{a}_j = \delta_{ij} \end{equation} $$

$$ \begin{equation} \text{Cov}(v_i, v_j) = \bm{b}_i^\top\bm{\Sigma}_{yy}\bm{b}_j = \delta_{ij} \end{equation} $$

ここで $\delta_{ij}$ はクロネッカーのデルタです。つまり、同じ変数群から作った異なる正準変量は互いに無相関で、分散は1です。

異なる変数群の正準変量間の無相関性:

$$ \begin{equation} \text{Cov}(u_i, v_j) = \bm{a}_i^\top\bm{\Sigma}_{xy}\bm{b}_j = \rho_i\delta_{ij} \end{equation} $$

異なる番号の正準変量間は無相関($i \neq j$ のとき共分散ゼロ)であり、同じ番号の正準変量間の共分散は正準相関 $\rho_i$ に等しくなります。

これらの性質は、CCAが「2つのデータ群の間の関係を、互いに独立な成分に分解する」という操作を行っていることを意味します。

正準変量の相関行列ヒートマップ。対角ブロックは単位行列、群間の対応番号だけρが立つ

図は、後のPython実装で得られる全正準変量の相関行列です。理論どおり、対角ブロック($u$ どうし・$v$ どうし)は単位行列で同じ群内の別の正準変量とは無相関、群をまたぐ部分は同じ番号のペアだけ $\rho_k$($u_1$-$v_1$ が0.98、$u_2$-$v_2$ が0.97)が立ち、異なる番号どうしはゼロになっています。CCAが関係を独立な成分へ分解している様子がひと目で分かります。

正準相関の解釈

正準相関 $\rho_k$ は以下のように解釈できます。

  • $\rho_1 = 1$: 2つの変数群に完全な線形関係がある方向が存在する
  • $\rho_1 = 0$: 2つの変数群はどの方向を取っても全く無相関
  • $0 < \rho_k < 1$: 第 $k$ の方向における線形関係の強さ

正準相関が $r$ 個だけ1に等しく、残りがゼロの場合、2つの変数群は $r$ 次元の共通構造を持っていることを意味します。

正準相関の有意性検定

正準相関が統計的に有意かどうかを検定するために、バートレットの逐次検定が使われます。帰無仮説 $H_0: \rho_k = \rho_{k+1} = \dots = \rho_{\min(p,q)} = 0$(第 $k$ 以降の正準相関がすべてゼロ)を検定します。

検定統計量はウィルクスのラムダに基づきます。

$$ \begin{equation} \Lambda_k = \prod_{i=k}^{\min(p,q)}(1 – \rho_i^2) \end{equation} $$

$$ \chi^2 = -\left(n – 1 – \frac{p + q + 1}{2}\right)\ln\Lambda_k $$

この統計量は近似的に自由度 $(p – k + 1)(q – k + 1)$ のカイ二乗分布に従います。

$k = 1$ から順に検定を行い、初めて帰無仮説が棄却されなくなった点で、有意な正準相関の数が決まります。これにより、2つの変数群間の関係が何次元で説明できるかが統計的に判断できます。

CCAと他の手法との関係

CCAは多変量統計学の他の手法と深い関係があります。

  • PCAとの関係: $\bm{y} = \bm{x}$ と設定すると、CCAの問題はデータの自己相関を最大化する問題になり、PCAと密接に関連します
  • 判別分析との関係: $\bm{y}$ をクラスラベルのダミー変数行列にすると、CCAはフィッシャーの判別分析と等価になります
  • 重回帰分析との関係: $\bm{y}$ が1変数の場合、第1正準相関は重相関係数に等しくなります

このような関係は、CCAが多変量統計学において中心的な位置を占めていることを示しています。

CCAとPCA・判別分析・重回帰の関係を示す図。特別な場合として各手法を含む

図のように、CCAは多変量解析の交差点に位置します。$\bm{y}=\bm{x}$ とすればPCAに、$\bm{y}$ をクラスラベルのダミー変数にすればフィッシャーの判別分析に、$\bm{y}$ が1変数なら重回帰(重相関係数)に、それぞれ特別な場合として帰着します。CCAを理解しておくと、これらの手法を「2群間の相関最大化」という統一的な視点から見渡せるようになります。

これらの理論をPythonで実装して確認しましょう。

Pythonによる実装と可視化

合成データでのCCA

まず、既知の相関構造を持つ2組の合成データを生成し、CCAが正しくその構造を検出できるかを確認します。

import numpy as np
import matplotlib.pyplot as plt
from scipy import linalg

np.random.seed(42)
n = 300

# --- 潜在変数 ---
z1 = np.random.randn(n)
z2 = np.random.randn(n)

# --- 第1変数群 x (3変数) ---
X = np.column_stack([
    0.9 * z1 + 0.1 * np.random.randn(n),
    0.7 * z1 + 0.3 * z2 + 0.2 * np.random.randn(n),
    0.1 * z1 + 0.8 * z2 + 0.15 * np.random.randn(n),
])

# --- 第2変数群 y (4変数) ---
Y = np.column_stack([
    0.85 * z1 + 0.15 * np.random.randn(n),
    0.6 * z1 + 0.4 * z2 + 0.2 * np.random.randn(n),
    0.2 * z1 + 0.75 * z2 + 0.1 * np.random.randn(n),
    0.3 * np.random.randn(n),  # ほぼ独立なノイズ変数
])

# --- 中心化 ---
X -= X.mean(axis=0)
Y -= Y.mean(axis=0)

p, q = X.shape[1], Y.shape[1]

# --- 共分散行列の計算 ---
Sxx = X.T @ X / n
Syy = Y.T @ Y / n
Sxy = X.T @ Y / n
Syx = Sxy.T

# --- CCAの固有値問題 ---
# M1 = Sxx^{-1} Sxy Syy^{-1} Syx
Sxx_inv = np.linalg.inv(Sxx)
Syy_inv = np.linalg.inv(Syy)
M1 = Sxx_inv @ Sxy @ Syy_inv @ Syx

# 固有値分解
eigvals, eigvecs_a = np.linalg.eig(M1)

# 実数部のみ取り、降順に並べ替え
eigvals = np.real(eigvals)
eigvecs_a = np.real(eigvecs_a)
idx = np.argsort(eigvals)[::-1]
eigvals = eigvals[idx]
eigvecs_a = eigvecs_a[:, idx]

# 正準相関
rho = np.sqrt(np.maximum(eigvals, 0))

# 正準変量の重みベクトル a を正規化(a^T Sxx a = 1)
A = eigvecs_a[:, :min(p, q)]
for k in range(A.shape[1]):
    norm = np.sqrt(A[:, k] @ Sxx @ A[:, k])
    A[:, k] /= norm

# 対応する b を計算
B = np.zeros((q, min(p, q)))
for k in range(min(p, q)):
    if rho[k] > 1e-10:
        B[:, k] = (1.0 / rho[k]) * Syy_inv @ Syx @ A[:, k]
        norm = np.sqrt(B[:, k] @ Syy @ B[:, k])
        B[:, k] /= norm

# --- 正準変量の計算 ---
U = X @ A  # (n, min(p,q))
V = Y @ B  # (n, min(p,q))

print("=== 正準相関分析の結果 ===")
for k in range(min(p, q)):
    empirical_corr = np.corrcoef(U[:, k], V[:, k])[0, 1]
    print(f"正準相関 ρ_{k+1} = {rho[k]:.4f} "
          f"(検証: Corr(u_{k+1}, v_{k+1}) = {empirical_corr:.4f})")

この結果から、CCAが2つのデータ群間の関係構造を正しく検出していることが確認できます。第1正準相関が高い値を示すのは、両データ群が潜在変数 $z_1$ を共有しているためです。第2正準相関は $z_2$ の共有に対応します。第3正準相関はほぼゼロに近く、3番目以降の方向には意味のある相関がないことを示しています。実際に計算すると、正準相関は $\rho_1 = 0.984$、$\rho_2 = 0.971$、$\rho_3 = 0.049$ となります。

正準相関スペクトルの棒グラフ。ρ1・ρ2は高くρ3はほぼ0で共有潜在が2次元だと分かる

正準相関を大きい順に棒グラフにすると、$\rho_1, \rho_2$ が0.97以上と高く、$\rho_3$ が0.05とほぼゼロで、はっきりとした「崖」が見えます。この崖の位置が、2つのデータ群が共有する構造の次元数(ここでは2、まさに仕込んだ潜在変数 $z_1, z_2$ の数)を教えてくれます。PCAでスクリープロットの肘を見て次元を決めるのと同じ発想です。

CCAが取り出した正準変量が、本当に仕込んだ共有潜在を復元できているかも確かめてみましょう。

第1・第2正準変量が真の共有潜在z1・z2を高い相関で復元していることを示す散布図

図は、CCAが求めた第1・第2正準変量 $u_1, u_2$ を、データ生成時に仕込んだ真の共有潜在 $z_1, z_2$ と照らし合わせたものです。$u_1$ は $z_1$ と、$u_2$ は $z_2$ と、いずれも相関0.93以上で強く対応しています。CCAが「2つの群に共通して効いている隠れた要因」を、相関の高い順に取り出せていることが確認できます。

正準変量の可視化

import numpy as np
import matplotlib.pyplot as plt
from scipy import linalg

np.random.seed(42)
n = 300
z1 = np.random.randn(n)
z2 = np.random.randn(n)
X = np.column_stack([
    0.9*z1 + 0.1*np.random.randn(n),
    0.7*z1 + 0.3*z2 + 0.2*np.random.randn(n),
    0.1*z1 + 0.8*z2 + 0.15*np.random.randn(n),
])
Y = np.column_stack([
    0.85*z1 + 0.15*np.random.randn(n),
    0.6*z1 + 0.4*z2 + 0.2*np.random.randn(n),
    0.2*z1 + 0.75*z2 + 0.1*np.random.randn(n),
    0.3*np.random.randn(n),
])
X -= X.mean(axis=0); Y -= Y.mean(axis=0)
p, q = X.shape[1], Y.shape[1]
Sxx = X.T@X/n; Syy = Y.T@Y/n; Sxy = X.T@Y/n; Syx = Sxy.T
M1 = np.linalg.inv(Sxx) @ Sxy @ np.linalg.inv(Syy) @ Syx
eigvals, eigvecs_a = np.linalg.eig(M1)
eigvals = np.real(eigvals); eigvecs_a = np.real(eigvecs_a)
idx = np.argsort(eigvals)[::-1]; eigvals = eigvals[idx]; eigvecs_a = eigvecs_a[:, idx]
rho = np.sqrt(np.maximum(eigvals, 0))
A = eigvecs_a[:, :min(p,q)]
for k in range(A.shape[1]):
    norm = np.sqrt(A[:,k]@Sxx@A[:,k]); A[:,k] /= norm
B = np.zeros((q, min(p,q)))
for k in range(min(p,q)):
    if rho[k] > 1e-10:
        B[:,k] = (1.0/rho[k])*np.linalg.inv(Syy)@Syx@A[:,k]
        norm = np.sqrt(B[:,k]@Syy@B[:,k]); B[:,k] /= norm
U = X@A; V = Y@B

fig, axes = plt.subplots(1, 3, figsize=(16, 5))

# (a) 第1正準変量
ax = axes[0]
ax.scatter(U[:, 0], V[:, 0], alpha=0.4, s=15, color="steelblue")
ax.set_xlabel("$u_1$ (1st canonical variate of X)", fontsize=11)
ax.set_ylabel("$v_1$ (1st canonical variate of Y)", fontsize=11)
ax.set_title(f"(a) 1st canonical pair ($\\rho_1={rho[0]:.3f}$)", fontsize=12)
ax.grid(True, alpha=0.3)
ax.set_aspect("equal")

# (b) 第2正準変量
ax = axes[1]
ax.scatter(U[:, 1], V[:, 1], alpha=0.4, s=15, color="coral")
ax.set_xlabel("$u_2$ (2nd canonical variate of X)", fontsize=11)
ax.set_ylabel("$v_2$ (2nd canonical variate of Y)", fontsize=11)
ax.set_title(f"(b) 2nd canonical pair ($\\rho_2={rho[1]:.3f}$)", fontsize=12)
ax.grid(True, alpha=0.3)
ax.set_aspect("equal")

# (c) 第3正準変量
ax = axes[2]
ax.scatter(U[:, 2], V[:, 2], alpha=0.4, s=15, color="gray")
ax.set_xlabel("$u_3$ (3rd canonical variate of X)", fontsize=11)
ax.set_ylabel("$v_3$ (3rd canonical variate of Y)", fontsize=11)
ax.set_title(f"(c) 3rd canonical pair ($\\rho_3={rho[2]:.3f}$)", fontsize=12)
ax.grid(True, alpha=0.3)
ax.set_aspect("equal")

plt.tight_layout()
plt.savefig("cca_canonical_variates.png", dpi=150, bbox_inches="tight")
plt.show()

3つのグラフから、正準相関の意味が視覚的に確認できます。

  1. 第1正準変量(図a): $u_1$ と $v_1$ の散布図は明瞭な正の相関パターンを示しています。データ点が対角線に沿って分布しており、$\rho_1$ が高いことが視覚的にも確認できます

  2. 第2正準変量(図b): $u_2$ と $v_2$ の間にも相関がありますが、第1正準変量より弱くなっています。散布図の広がりが大きく、相関がやや低いことが見て取れます

  3. 第3正準変量(図c): $u_3$ と $v_3$ の間にはほとんど相関がありません。散布図はほぼ円形であり、この方向には2つの変数群間に意味のある線形関係がないことを示しています

第1〜第3正準対の散布図。番号が進むほど相関が弱まり第3対はほぼ無相関

3つの散布図を並べると、正準相関の減衰が視覚的にはっきりします。第1正準対($\rho_1=0.984$)は対角線上に密に乗り、第2対($\rho_2=0.971$)もまだ強い相関を保ちますが、第3対($\rho_3=0.049$)はほぼ円形に散らばり、相関が失われています。番号が進むほど群間の関係が弱くなるという、正準相関の順序構造がそのまま現れています。

正準変量間の無相関性の検証

正準変量の理論的性質(直交性と無相関性)を数値的に検証します。

import numpy as np

np.random.seed(42)
n = 300
z1 = np.random.randn(n)
z2 = np.random.randn(n)
X = np.column_stack([
    0.9*z1 + 0.1*np.random.randn(n),
    0.7*z1 + 0.3*z2 + 0.2*np.random.randn(n),
    0.1*z1 + 0.8*z2 + 0.15*np.random.randn(n),
])
Y = np.column_stack([
    0.85*z1 + 0.15*np.random.randn(n),
    0.6*z1 + 0.4*z2 + 0.2*np.random.randn(n),
    0.2*z1 + 0.75*z2 + 0.1*np.random.randn(n),
    0.3*np.random.randn(n),
])
X -= X.mean(axis=0); Y -= Y.mean(axis=0)
p, q = X.shape[1], Y.shape[1]
Sxx = X.T@X/n; Syy = Y.T@Y/n; Sxy = X.T@Y/n; Syx = Sxy.T
M1 = np.linalg.inv(Sxx) @ Sxy @ np.linalg.inv(Syy) @ Syx
eigvals, eigvecs_a = np.linalg.eig(M1)
eigvals = np.real(eigvals); eigvecs_a = np.real(eigvecs_a)
idx = np.argsort(eigvals)[::-1]; eigvals = eigvals[idx]; eigvecs_a = eigvecs_a[:, idx]
rho = np.sqrt(np.maximum(eigvals, 0))
A = eigvecs_a[:, :min(p,q)]
for k in range(A.shape[1]):
    norm_val = np.sqrt(A[:,k]@Sxx@A[:,k]); A[:,k] /= norm_val
B = np.zeros((q, min(p,q)))
for k in range(min(p,q)):
    if rho[k] > 1e-10:
        B[:,k] = (1.0/rho[k])*np.linalg.inv(Syy)@Syx@A[:,k]
        norm_val = np.sqrt(B[:,k]@Syy@B[:,k]); B[:,k] /= norm_val
U = X@A; V = Y@B

# 全正準変量の相関行列
UV = np.column_stack([U, V])
corr_matrix = np.corrcoef(UV.T)

labels = [f"$u_{k+1}$" for k in range(3)] + [f"$v_{k+1}$" for k in range(3)]

print("=== 正準変量間の相関行列 ===")
print(f"{'':>6s}", end="")
for l in labels:
    print(f"{l:>8s}", end="")
print()
for i in range(6):
    print(f"{labels[i]:>6s}", end="")
    for j in range(6):
        print(f"{corr_matrix[i,j]:8.3f}", end="")
    print()

この相関行列の結果は、理論から予測される正準変量の性質を見事に確認しています。

  1. 対角成分はすべて1.0: 各正準変量の分散は1に正規化されています
  2. 同じ群内の異なる正準変量間はほぼ0: $\text{Corr}(u_1, u_2) \approx 0$、$\text{Corr}(v_1, v_2) \approx 0$ など。これは正準変量が同じ群内で無相関であることを示しています
  3. 異なる群の同じ番号は $\rho_k$: $\text{Corr}(u_1, v_1) \approx \rho_1$、$\text{Corr}(u_2, v_2) \approx \rho_2$ など。対応する正準変量ペアの相関は正準相関に一致しています
  4. 異なる群の異なる番号はほぼ0: $\text{Corr}(u_1, v_2) \approx 0$ など。異なる番号の正準変量間は群をまたいでも無相関です

これらの結果は、CCAが2つのデータ群間の関係を独立な成分に分解する操作を正しく行っていることの数値的な検証です。

まとめ

本記事では、正準相関分析(CCA)の理論を導出しました。

  • CCAの目的: 2組の変数群 $\bm{x}$ と $\bm{y}$ の間の関係を、最大相関の方向から順に抽出する手法です
  • 定式化: 正準変量 $u = \bm{a}^\top\bm{x}$, $v = \bm{b}^\top\bm{y}$ の相関を最大化する制約付き最適化問題として定式化されます
  • 導出: ラグランジュ乗数法により、行列 $\bm{\Sigma}_{xx}^{-1}\bm{\Sigma}_{xy}\bm{\Sigma}_{yy}^{-1}\bm{\Sigma}_{yx}$ の固有値問題に帰着します。固有値の平方根が正準相関です
  • 正準変量の性質: 同じ群内の異なる正準変量は無相関、異なる群の同じ番号の正準変量の相関は正準相関 $\rho_k$、異なる番号は無相関です
  • CCAはPCAの自然な拡張であり、1つのデータ群の分散構造ではなく、2つのデータ群間の相関構造を分析する手法です

次のステップとして、以下の記事も参考にしてください。