ある銘柄が動いたとき、別の銘柄も同じ方向に動くか。この「連動の強さ」を測る基本指標が共分散(covariance)と相関係数(correlation coefficient)です。本記事では定義と pandas での計算、相関行列の可視化までを整理します。

目次

  1. 共分散の定義
  2. 相関係数の定義
  3. Python で計算する
  4. 相関行列のヒートマップ
  5. 相関の落とし穴
  6. ローリング相関

共分散の定義

2 つの変数 XXYY の共分散は次のとおりです。

Cov(X,Y)=1ni=1n(xixˉ)(yiyˉ)\mathrm{Cov}(X, Y) = \frac{1}{n} \sum_{i=1}^{n} (x_i - \bar{x})(y_i - \bar{y})
  • 同じ方向に動く傾向 → 正
  • 逆方向に動く傾向 → 負
  • 無関係 → ゼロ付近

ただし共分散は 単位の影響 を受けます。リターン同士なら %² 相当の単位になり、大きさだけ見ても解釈しづらいのが欠点です。

相関係数の定義

共分散を、各変数の標準偏差で割って単位をなくしたものが相関係数(ピアソンの相関係数)です。

ρXY=Cov(X,Y)σXσY\rho_{XY} = \frac{\mathrm{Cov}(X, Y)}{\sigma_X \sigma_Y}

値の範囲は 1ρ1-1 \le \rho \le 1 で、解釈は次のとおりです。

値の範囲解釈
+1+1完全な正の連動
+0.7+0.7+1+1強い正の連動
+0.3+0.3+0.7+0.7中程度の正の連動
0.3-0.3+0.3+0.3ほぼ無関係
0.7-0.70.3-0.3中程度の負の連動
1-10.7-0.7強い負の連動
1-1完全な負の連動

ここで重要なのは、相関は線形的な関係しか捉えない ことです。U 字型の関係や、極端な領域だけで連動するケースは値が小さく出ることがあります。

相関係数の値ごとの散布図イメージ

Python で計算する

3 銘柄分のサンプルリターンを作って、共分散と相関を計算します。

import numpy as np
import pandas as pd
rng = np.random.default_rng(7)
n = 250 # 約 1 年分の営業日
# A は基本トレンド、B は A と連動、C はほぼ独立
base = rng.normal(0.0, 0.01, size=n)
returns = pd.DataFrame({
"A": base + rng.normal(0.0, 0.005, size=n),
"B": base + rng.normal(0.0, 0.005, size=n),
"C": rng.normal(0.0, 0.012, size=n),
})
cov_matrix = returns.cov()
corr_matrix = returns.corr()
print("共分散行列:")
print(cov_matrix.round(6))
print("\n相関行列:")
print(corr_matrix.round(3))

共分散行列の対角は分散、相関行列の対角は常に 1 です。

相関行列のヒートマップ

銘柄数が増えると数字を読み取るのが大変になります。matplotlib のヒートマップが見やすい選択肢です。

import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(5, 4))
im = ax.imshow(corr_matrix, vmin=-1, vmax=1, cmap="coolwarm")
ax.set_xticks(range(len(corr_matrix.columns)))
ax.set_yticks(range(len(corr_matrix.columns)))
ax.set_xticklabels(corr_matrix.columns)
ax.set_yticklabels(corr_matrix.columns)
# セルに数値を重ねる
for i in range(len(corr_matrix)):
for j in range(len(corr_matrix)):
ax.text(j, i, f"{corr_matrix.iat[i, j]:.2f}", ha="center", va="center")
fig.colorbar(im, ax=ax)
ax.set_title("Correlation matrix")
plt.tight_layout()
plt.show()

色のスケールを 1-1 から +1+1 に固定しておくと、銘柄を入れ替えても色味の意味が一貫します。

3 銘柄の相関行列ヒートマップ

相関の落とし穴

実務でよく踏みやすい落とし穴を挙げます。

  • 相関は因果ではない: 連動しているからといって、片方がもう片方を引き起こしているとは限りません
  • 欠損日の扱い: 片方だけデータがある日を含めると、計算がずれる可能性があります(dropnaalign で揃える)
  • 非線形関係: ピアソン相関は線形関係しか捉えません。順序関係を見るならスピアマンの順位相関を検討します
  • 時間依存性: 相関は期間によって大きく変わります。直近 1 年と 5 年では別物になることがあります
  • 見せかけの相関: 共通のトレンド(地合い・指数)で動いているだけのケースもあります

ローリング相関

相関の時間変化を追うには、ローリング相関が便利です。

rolling_corr = returns["A"].rolling(window=60).corr(returns["B"])
fig, ax = plt.subplots(figsize=(8, 3))
ax.plot(rolling_corr.index, rolling_corr.values)
ax.set_title("Rolling 60-day correlation: A vs B")
ax.axhline(0, color="gray", linewidth=0.5)
plt.tight_layout()
plt.show()

相関が大きく変わる時期は、市場環境(高ボラ局面など)の変化と一致することが多いです。

注意点

  • 相関は便利だが、絶対視しないこと
  • ポートフォリオのリスク評価では、相関 + 分散の両方が必要
  • 銘柄の選び方によっては「ほぼ全銘柄が高相関」になることがあります(同じ業種に偏ったときなど)

生成AI へのプロンプト例

相関行列とヒートマップを 1 つの関数にまとめる例です。

日次リターンの pandas DataFrame を受け取り、相関行列を計算したうえで、
matplotlib でヒートマップを描画する関数 plot_corr_heatmap(returns) を書いてください。
要件:
- 戻り値は相関の DataFrame
- 色スケールは -1 から +1 に固定
- セルに数値を重ねる
- pandas 2.2 系の API を使う
- docstring を日本語で書く

まとめ

  • 共分散は連動の方向を、相関係数はスケールフリーな強さを表す
  • 相関係数は 1-1 から +1+1 の範囲で、線形関係を捉える
  • 銘柄数が増えたらヒートマップで可視化
  • 相関 ≠ 因果、相関は時期によって変わる、欠損日には注意