8  相関

これまでは,代表値やばらつきを使って,一つの変数の特徴を調べてきた.本章では,二つの変数の関係に注目する.

例えば,最高気温が高い日は,最低気温も高いのだろうか.前章と同じ東京の 8 月のデータを使い,まず散布図で関係を見る.その後,共分散と相関係数を使って,関係を数値で表す方法を学ぶ.

8.1 最高気温と最低気温

チャプター 6 と前章では,2025 年の東京の 8 月の最高気温 31 日分を使った.ここでは,同じ日の最低気温も取り出す.

tokyo_daily.csv をダウンロードし,チャプター 4 と同じ手順で Colab にアップロードする.このファイルは,気象庁の観測データを加工したものである.本章のコードは,上から順に実行する.

import pandas as pd
import matplotlib.pyplot as plt

daily = pd.read_csv("tokyo_daily.csv")
august = daily[daily["month"] == 8]
august[["date", "temp_max_c", "temp_min_c"]].head(7)
date temp_max_c temp_min_c
212 2025-08-01 30.1 23.8
213 2025-08-02 35.1 24.9
214 2025-08-03 36.1 26.2
215 2025-08-04 36.1 27.7
216 2025-08-05 36.6 28.3
217 2025-08-06 37.0 28.1
218 2025-08-07 32.8 27.3

1 行が 1 日を表し,temp_max_c が最高気温,temp_min_c が最低気温である.どちらも単位は℃である.例えば,8 月 1 日は最高気温が 30.1℃,最低気温が 23.8℃ なので,この二つを一組として扱う.

august_max = august["temp_max_c"]
august_min = august["temp_min_c"]

二つの変数の関係を調べるには,同じ対象について測った値を組にすることが大切である.ここでは,同じ日の最高気温と最低気温を対応させる.それぞれの列を別々に並べ替え,行の対応を無視して順番だけで組にすると,異なる日の値を比べることになってしまう.

8.2 散布図で関係を見る

散布図(scatter plot)は,二つの数値変数を横軸と縦軸に取り,一組のデータを一つの点で表すグラフである.

例 8.1 最高気温を横軸,最低気温を縦軸として,8 月の 31 日分を散布図にしてみよう.plt.scatter() の第 1 引数に横軸のデータ,第 2 引数に縦軸のデータを渡す.

plt.figure(figsize=(6, 4))
plt.scatter(august_max, august_min)
plt.xlabel("Maximum temperature (°C)")
plt.ylabel("Minimum temperature (°C)")
plt.grid(alpha=0.3)
plt.tight_layout()
plt.show()
横軸は最高気温で27.7から38.5℃,縦軸は最低気温で23.8から28.3℃.31日分の点は,ばらつきながら全体として右上がりに分布している.
図 8.1: 東京の 8 月の最高気温と最低気温(2025 年).1 点が同じ日の二つの気温を表す.

図 8.1 では,点が全体として右上がりに分布している.この 31 日間では,最高気温が高い日ほど,最低気温も高い傾向がある.ただし,点は一直線に並んでいるわけではなく,最高気温が近くても最低気温が異なる日がある.

一方の値が大きいほど,もう一方も大きい傾向を正の相関,一方の値が大きいほど,もう一方が小さい傾向を負の相関という.

散布図での見え方 関係の読み方
全体として右上がり 正の相関がある
全体として右下がり 負の相関がある
右上がり・右下がりの傾向がはっきりしない 直線的な関係が弱い可能性がある

相関は全体の傾向を表すものであり,すべての点がその傾向に従うとは限らない.また,点が曲線状に並ぶこともあるため,直線的な傾向だけでなく,分布の形も確かめよう.

8.3 共分散

前章の分散では,各データと平均値との差を二乗した.二つの変数の関係を調べるときは,それぞれの平均との差を掛け合わせてみよう.

8.3.1 差の積の符号

最高気温を x,最低気温を y とし,それぞれの平均値を \bar{x},\bar{y} と書く.次のコードで平均値を求めると,最高気温は約 34.4℃,最低気温は約 26.2℃ となる.

mean_max = august_max.mean()
mean_min = august_min.mean()
print("最高気温の平均値(℃):", round(mean_max, 1))
print("最低気温の平均値(℃):", round(mean_min, 1))
最高気温の平均値(℃): 34.4
最低気温の平均値(℃): 26.2

両方が平均値より高い日は,差の積 (x-\bar{x})(y-\bar{y}) が正になる.両方が平均値より低い日も,負の数どうしを掛けるので,積は正になる.それに対し,一方が平均値より高く,もう一方が低い日は,積が負になる.

最高気温 最低気温 差の積の符号
平均値より高い 平均値より高い 正
平均値より低い 平均値より低い 正
平均値より高い 平均値より低い 負
平均値より低い 平均値より高い 負

どちらかの値が平均値と等しいとき,積は 0 になる.散布図に二つの平均値の線を加えると,この符号を確かめられる.

コード
plt.figure(figsize=(6, 4))
plt.scatter(august_max, august_min)
plt.axvline(mean_max, color="gray", linestyle="--")
plt.axhline(mean_min, color="gray", linestyle="--")
plt.text(38.5, 28.6, "+", fontsize=18, ha="center")
plt.text(28.0, 23.6, "+", fontsize=18, ha="center")
plt.text(28.0, 28.6, "−", fontsize=18, ha="center")
plt.text(38.5, 23.6, "−", fontsize=18, ha="center")
plt.xlim(27, 40)
plt.ylim(23, 29.5)
plt.xlabel("Maximum temperature (°C)")
plt.ylabel("Minimum temperature (°C)")
plt.tight_layout()
plt.show()
最高気温と最低気温の散布図を,最高気温の平均約34.4℃の縦線と最低気温の平均約26.2℃の横線で四つに分けている.右上と左下に正,左上と右下に負の符号がある.
図 8.2: 平均との差の積.右上と左下の領域では正,左上と右下の領域では負になる.破線はそれぞれの平均値を表す.

例えば,8 月 1 日の点 (30.1, 23.8) は左下,8 月 6 日の点 (37.0, 28.1) は右上の領域にある.どちらも差の積は正である.図 8.2 のように,正の積の合計が負の積の絶対値の合計を上回ると,積の平均は正になる.単に点の個数を比べるのではなく,平均値からの離れ方も含めて考えている.

8.3.2 共分散

それぞれの平均との差の積を平均した値を,共分散(covariance)という.n 組のデータ (x_1,y_1),\ldots,(x_n,y_n) に対し,本章では分母を n とする共分散を s_{xy,n} と書く.前章で扱った,分母が n の標本分散と同じ考え方である.

s_{xy,n}=\frac{1}{n}\sum_{i=1}^{n}(x_i-\bar{x})(y_i-\bar{y})

計算の手順は,「それぞれの平均値を引く」「対応する差を掛ける」「積を平均する」の三つである.

max_deviation = august_max - mean_max
min_deviation = august_min - mean_min
covariance = (max_deviation * min_deviation).mean()
print("共分散(℃²):", round(covariance, 2))
共分散(℃²): 1.91

august_max - mean_max のように,Series から一つの数値を引くと,各要素からその数値が引かれる.+,*,/,** でも同じように要素ごとに計算され,NumPy 配列でも同様である.また,max_deviation * min_deviation では,同じ日の差どうしが掛けられる.

共分散は約 1.91℃^2 となり,正の値である.これは,散布図で見た右上がりの傾向と対応している.分散と異なり,共分散は負になることもある.

ただし,共分散の大きさは単位に依存する.例えば,身長と体重の共分散では,身長を m から cm に直すだけで,身長の平均値も平均との差も 100 倍になり,共分散も 100 倍になる.このため,共分散の値だけでは,異なる単位の変数どうしの関係の強さを比べにくい.

8.4 相関係数

共分散を二つの変数の標準偏差の積で割ると,単位の影響を取り除ける.この値を相関係数(correlation coefficient)という.本章で扱うものは,ピアソンの相関係数とも呼ばれ,二つの変数の直線的な関係の向きと強さを表す.

前章と同様に,分母が n の分散から求めた標準偏差を s_{x,n},s_{y,n} とすると,相関係数 r は次のようになる.

r=\frac{s_{xy,n}}{s_{x,n}s_{y,n}} =\frac{\sum_{i=1}^{n}(x_i-\bar{x})(y_i-\bar{y})} {\sqrt{\sum_{i=1}^{n}(x_i-\bar{x})^2}\sqrt{\sum_{i=1}^{n}(y_i-\bar{y})^2}}

気温の例では,共分散の単位は℃^2,標準偏差の積の単位も℃^2 なので,割り算によって単位がなくなる.

Series の .corr() を使うと,相関係数を直接求められる.一方の Series に対し,もう一方を引数として渡す.

r_august = august_max.corr(august_min)
print("8月の相関係数:", round(r_august, 2))
8月の相関係数: 0.66

相関係数は約 0.66 である.この 31 日間には,最高気温が高い日ほど最低気温も高いという,正の相関がある.0.66 は,最低気温が最高気温の 66% になるという意味でも,66% の日で両方の気温が高かったという意味でもない.

共分散と標準偏差から計算しても,同じ値になる.

std_max = august_max.std(ddof=0)
std_min = august_min.std(ddof=0)
r_from_covariance = covariance / (std_max * std_min)
print("共分散と標準偏差から計算:", round(r_from_covariance, 2))
共分散と標準偏差から計算: 0.66
ノート分母をそろえる

pandas の .std() は,省略時には ddof=1,つまり分母が n-1 の分散の平方根を計算する.上のコードでは,分母が n の共分散に合わせて ddof=0 を指定した.共分散と二つの分散の分母をすべて n-1 にそろえても,相関係数ではその係数が打ち消し合うため,結果は同じになる.

ノート対応する行と欠損値

pandas の .corr() は,二つの Series の行ラベル(インデックス)を対応させて計算する.同じ表から二つの列を取り出すと,元の行の対応を保ちやすい.どちらかの値が欠けている組は,計算から除かれる.本章の 8 月の二つの気温には欠損値がなく,31 組すべてを使っている.

後の章で使う NumPy の np.corrcoef(x, y)[0, 1] でも,同じ相関係数を求められる.np.corrcoef() は二つの変数の相関を表にした 2 行 2 列の配列を返し,[0, 1] で 1 行目・2 列目の値を取り出す.

import numpy as np

print("NumPyで確認:", round(np.corrcoef(august_max, august_min)[0, 1], 2))
NumPyで確認: 0.66

8.4.1 相関係数の読み方

相関係数は -1\le r\le1 の範囲の値を取る.符号が関係の向き,絶対値が直線的な関係の強さを表す.

相関係数 読み方
r が 1 に近い 強い正の相関がある
r が -1 に近い 強い負の相関がある
r が 0 に近い 直線的な関係が弱い

例えば,r=-0.9 は r=0.3 より直線的な関係が強い.負だから弱いというわけではない.また,r=1 ならすべての点が右上がりの直線上に,r=-1 なら右下がりの直線上に並ぶ.ただし,どちらかの変数の値がすべて同じ場合は,標準偏差が 0 になるため,相関係数を定義できない.

8.5 相関係数における注意点

8.5.1 非線形な関係

次の図は,説明のために作成した三つのデータである.左と中央では点が一直線に並んでいる.右では,y=x^2 という関係がある.

コード
x_example = pd.Series([-3, -2, -1, 0, 1, 2, 3])
y_examples = [x_example, -x_example, x_example ** 2]
titles = ["y = x", "y = -x", "y = x²"]

fig, axes = plt.subplots(1, 3, figsize=(10, 3))
for ax, y_example, title in zip(axes, y_examples, titles):
    r = x_example.corr(y_example)
    ax.scatter(x_example, y_example)
    ax.set_title(f"{title}  (r = {r:.2f})")
    ax.set_xlabel("x")
    ax.set_ylabel("y")
    ax.grid(alpha=0.3)
plt.tight_layout()
plt.show()
三つの散布図.左は右上がりの直線,中央は右下がりの直線,右はU字型に点が並んでいる.各図の相関係数は順に1,-1,0である.
図 8.3: 相関係数と散布図の形.左は r=1,中央は r=-1,右は r=0 だが,右にも曲線状の関係がある.

図 8.3 の右の図は,相関係数が 0 でも,二つの変数の間に関係がある例である.相関係数は直線的な関係を表すため,曲線状の関係を十分に捉えられない.また,少数の極端な点に影響されることもある.相関係数と散布図を合わせて確認しよう.

8.5.2 相関関係と因果関係

二つの変数に相関があっても,一方が他方の原因であるとは限らない.例えば,暑い日ほどアイスクリームも冷たい飲み物もよく売れるとしよう.両方の売上に正の相関があっても,それだけで「アイスクリームが売れるから,冷たい飲み物も売れる」とはいえない.気温という別の要因が,両方に関係している可能性がある.

同じように,最高気温と最低気温に正の相関があることだけから,一方が他方を高くするとは結論できない.相関係数では横軸と縦軸を入れ替えても値は同じであり,原因と結果の向きはわからない.原因を考えるには,ほかの要因や時間の前後関係,データを集めた方法も調べる必要がある.

8.6 練習問題

練習 8.1 (9 月の気温の関係を調べる) daily から,9 月の最高気温と最低気温を取り出す.

september = daily[daily["month"] == 9]
september_max = september["temp_max_c"]
september_min = september["temp_min_c"]
  1. 最高気温を横軸,最低気温を縦軸とする散布図を描け.軸名と単位も付けること.
  2. 相関係数を求め,8 月の結果と比べよ.
  3. この結果から読み取れる傾向を一文で説明せよ.また,相関係数だけでは判断できないことを一つ挙げよ.

練習 8.2 (アヤメの大きさを調べる) 次のコードは,アヤメの一種である setosa のデータを読み込む.1 行が一つの花を表す.setosa_sepal_length はがく片の長さ,setosa_sepal_width はがく片の幅であり,単位はどちらも cm である.

from sklearn.datasets import load_iris

iris = load_iris(as_frame=True)
setosa = iris.data[iris.target == 0]
setosa_sepal_length = setosa["sepal length (cm)"]
setosa_sepal_width = setosa["sepal width (cm)"]
  1. 長さを横軸,幅を縦軸とする散布図を描き,相関係数を求めよ.
  2. 長さだけを mm に直し,相関係数をもう一度求めよ.元の値と比べ,その理由を説明せよ.

練習 8.3 (相関係数を自分で計算する(発展)) 同じ長さの二つの NumPy 配列 x,y を受け取り,相関係数を返す関数 my_corrcoef() を定義せよ.それぞれの配列には 2 個以上の数値があり,欠損値はなく,どちらもすべて同じ値ではないものとする.

  1. それぞれの平均値を引いた配列を作る.
  2. 差の積の合計を求める.
  3. 差の二乗の合計をそれぞれ求め,相関係数の式に代入する.平方根は np.sqrt() で求められる.
def my_corrcoef(x, y):
    # ここにコードを書く
    return r

x = np.array([-3, -2, -1, 0, 1, 2, 3])

y = x,y = -x,y = x ** 2 の三つの場合を調べ,np.corrcoef(x, y)[0, 1] と結果が一致することを確かめよ.

8.7 まとめ

  • 散布図では,同じ対象から得た二つの値を一つの点として表す.
  • 共分散は,それぞれの平均との差の積を平均した値である.正にも負にもなり,単位に依存する.
  • 相関係数は,共分散を二つの標準偏差の積で割った値であり,単位をもたない.pandas では .corr() で求められる.
  • 相関係数の符号は直線的な関係の向き,絶対値はその強さを表す.0 に近くても,曲線状の関係があるかもしれない.
  • 相関関係だけでは因果関係は判断できない.散布図や,データを集めた条件も合わせて考える.

ここまでで,データを読み込み,グラフと数値で特徴を調べる方法を学んだ.次章からは,これらを組み合わせて実際の問いを考える.気温とさくらの開花日の関係は,チャプター 10 で分析する.

ヒント相関係数の計算方法を調べる

pandas の Series.corr() と NumPy の corrcoef() の公式ドキュメントには,引数や使用例がある.標準偏差の ddof の指定は,Series.std() で確認できる.