ある銘柄が動いたとき、別の銘柄も同じ方向に動くか。この「連動の強さ」を測る基本指標が共分散(covariance)と相関係数(correlation coefficient)です。本記事では定義と pandas での計算、相関行列の可視化までを整理します。
目次
- 共分散の定義
- 相関係数の定義
- Python で計算する
- 相関行列のヒートマップ
- 相関の落とし穴
- ローリング相関
共分散の定義
2 つの変数 、 の共分散は次のとおりです。
- 同じ方向に動く傾向 → 正
- 逆方向に動く傾向 → 負
- 無関係 → ゼロ付近
ただし共分散は 単位の影響 を受けます。リターン同士なら %² 相当の単位になり、大きさだけ見ても解釈しづらいのが欠点です。
相関係数の定義
共分散を、各変数の標準偏差で割って単位をなくしたものが相関係数(ピアソンの相関係数)です。
値の範囲は で、解釈は次のとおりです。
| 値の範囲 | 解釈 |
|---|---|
| 完全な正の連動 | |
| 〜 | 強い正の連動 |
| 〜 | 中程度の正の連動 |
| 〜 | ほぼ無関係 |
| 〜 | 中程度の負の連動 |
| 〜 | 強い負の連動 |
| 完全な負の連動 |
ここで重要なのは、相関は線形的な関係しか捉えない ことです。U 字型の関係や、極端な領域だけで連動するケースは値が小さく出ることがあります。
Python で計算する
3 銘柄分のサンプルリターンを作って、共分散と相関を計算します。
import numpy as npimport 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()色のスケールを から に固定しておくと、銘柄を入れ替えても色味の意味が一貫します。
相関の落とし穴
実務でよく踏みやすい落とし穴を挙げます。
- 相関は因果ではない: 連動しているからといって、片方がもう片方を引き起こしているとは限りません
- 欠損日の扱い: 片方だけデータがある日を含めると、計算がずれる可能性があります(
dropnaやalignで揃える) - 非線形関係: ピアソン相関は線形関係しか捉えません。順序関係を見るならスピアマンの順位相関を検討します
- 時間依存性: 相関は期間によって大きく変わります。直近 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 を日本語で書くまとめ
- 共分散は連動の方向を、相関係数はスケールフリーな強さを表す
- 相関係数は から の範囲で、線形関係を捉える
- 銘柄数が増えたらヒートマップで可視化
- 相関 ≠ 因果、相関は時期によって変わる、欠損日には注意