Python相関分析入門|勉強時間と点数の関係を調べる

python
データ分析シリーズ 第2弾

Python相関分析入門|勉強時間と点数の関係を調べる

「2つの数字に関係があるか」を調べる方法を、身近な例で体験します

プログラミング未経験OK 数式は使いません 所要時間の目安:35分程度

前回の記事(Pythonでデータ分析の基礎を身につける方法)では、平均・合計・グループ集計・グラフ化という基本操作を紹介しました。今回はその続編として、「2つの数字が一緒に動くかどうか」を調べる相関分析を、勉強時間と点数という身近な例で体験します。

相関分析は、日々の事務作業を自動化する話から一歩進んだ、「集めたデータから何かを言うための技術」です。売上と広告費、来客数と気温、作業時間とミスの件数——手元にある2列の数字から傾向を読み取れるようになると、報告書の説得力が変わります。

新しいライブラリのインストールは不要で、前回と同じpandas・matplotlibだけで完結します。数式も出てきません。

  1. 1
  2. 2
  3. 3
  4. 4

相関分析って何をすること?

相関分析とは、ひとことで言うと「片方の数字が大きくなると、もう片方の数字も大きくなる(または小さくなる)傾向があるか」を調べることです。たとえば、次のような組み合わせについて、なんとなく関係がありそうだと感じたことはないでしょうか。

  • 勉強時間が長い人ほど、テストの点数が高い
  • 気温が高い日ほど、アイスクリームの売上が伸びる
  • 広告費をかけるほど、商品の売上が伸びる

相関分析を使うと、こうした「なんとなくの印象」を、具体的な数字で確認できるようになります。「関係がある気がします」と言うのと、「相関係数0.96で、強い正の相関があります」と言うのとでは、話の重みがまったく違います。

MEMO

相関分析は、前回のデータ分析の基礎で学んだ「集める→整理する→傾向をつかむ」という3ステップのうち、「傾向をつかむ」をより深掘りする内容です。難しい数式は一切使いません。

用意するもの

  • Python本体
  • コードを書くためのエディタ(VS Codeなど)
  • pandas・matplotlib
INFO

前回の記事と同じライブラリだけで進められるので、既に環境がある方は追加のインストールは不要です。まだの方は、先に「Pythonを始める前に:インストールとエディタの準備」を済ませてください。

pip install pandas matplotlib

グラフの中の日本語が豆腐のような四角(□□□)にならないようにするため、コードの中で日本語フォントを1行指定します(次の章で出てきます)。追加のライブラリは要りません。

使うデータを用意する

今回は、「勉強時間」と「テストの点数」の関係を例にします。「名前」「勉強時間」「点数」の3列だけのシンプルな形式です。

名前勉強時間(時間)点数
Aさん155
Bさん262
Cさん368
Dさん374
Eさん478
Fさん585
Gさん690
Hさん683

CSVファイルの作り方

Excelで上の表を作り、「名前を付けて保存」でファイルの種類を「CSV UTF-8(コンマ区切り)」にして、study_scores.csvという名前で保存してください。メモ帳で直接書く場合は、次の内容をそのまま貼り付けます。

名前,勉強時間,点数
Aさん,1,55
Bさん,2,62
Cさん,3,68
Dさん,3,74
Eさん,4,78
Fさん,5,85
Gさん,6,90
Hさん,6,83
INFO

1行目の見出しは、コードの中でdf["勉強時間"]のように列を指定するときの名前になります。ここに余計なスペースが入っていると、列が見つからずエラーになります。カンマの前後は詰めて書いてください。

MEMO

数字と数字の組み合わせであれば、身長と体重、広告費と売上、気温と来客数など、どんなデータでも同じ手順で分析できます。列の名前を変えるだけで、この記事のコードはそのまま使えます。

散布図で全体像をつかむ

相関係数をいきなり求める前に、まずは「散布図」を描いて目で見てみましょう。散布図は、2つの数字の組み合わせを点でプロットしたグラフです。

数字を出す前に、必ず絵で見る。これは相関分析の鉄則です。理由は後の「外れ値ひとつで、結論がひっくり返る」で分かります。

import pandas as pd
import tkinter as tk
import matplotlib
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

# グラフの日本語が□□□にならないようにする(Windows標準のフォントを指定)
matplotlib.rcParams["font.family"] = "Meiryo"

df = pd.read_csv("study_scores.csv", encoding="utf-8-sig")

fig = Figure(figsize=(6, 4))
ax = fig.add_subplot(111)
ax.scatter(df["勉強時間"], df["点数"], color="#3e8f5c", s=80)
ax.set_title("勉強時間と点数の関係")
ax.set_xlabel("勉強時間(時間)")
ax.set_ylabel("点数")
fig.tight_layout()
fig.savefig("study_scatter.png")

root = tk.Tk()
root.title("勉強時間と点数の関係")
canvas = FigureCanvasTkAgg(fig, master=root)
canvas.draw()
canvas.get_tk_widget().pack()
root.mainloop()

実行すると、点が右上がりに並んでいる様子が見えるはずです。「勉強時間が増えるほど、点数も高くなる傾向がありそうだ」ということが、数字を計算する前から視覚的に確認できます。

実際に動かすと、こうなります

勉強時間と点数の散布図。左下から右上へ、8つの点が右上がりに並んでいる
実際に上のコードを動かして作られた散布図です。この記事の見本CSV(8人分)をそのまま使っています。
POINT

点が右上がりに並んでいれば「正の相関」、右下がりに並んでいれば「負の相関」、バラバラに散らばっていれば「相関なし」の可能性が高い、というのが散布図から読み取れる大まかな目安です。

MEMO

encoding="utf-8-sig"は、ExcelでCSVを保存したときに先頭に付く目に見えない印(BOM)を、正しく読み飛ばすための指定です。これを付けずにutf-8で読むと、1列目の名前が名前ではなく変な文字になり、列が見つからないエラーになります。

相関係数を求める

散布図で見た「なんとなくの傾向」を、具体的な数字にしてみます。pandasのcorr()を使うと、たった1行で相関係数が求められます。

corr = df["勉強時間"].corr(df["点数"])
print("相関係数:", round(corr, 4))

実行すると、次のように表示されます。

相関係数: 0.9637

この数字が「相関係数」で、-1から1までの範囲で、2つの数字の関係の強さを表します。1に近いほど「片方が増えると、もう片方もきれいに増える」関係が強いということです。

MEMO

round(corr, 4)は小数点以下4桁に丸める指定です。付けないと0.9637139...のように長く表示されます。報告書に載せるときは、2桁(0.96)程度で十分です。

相関係数の読み方

相関係数がどのくらいの数字だと、どのくらいの関係の強さなのか、目安は以下の通りです。

相関係数の目安関係の強さ
0.7 ~ 1.0強い正の相関
0.4 ~ 0.7中くらいの正の相関
0.2 ~ 0.4弱い正の相関
-0.2 ~ 0.2ほぼ相関なし
-0.4 ~ -0.2弱い負の相関
-0.7 ~ -0.4中くらいの負の相関
-1.0 ~ -0.7強い負の相関

今回の例では0.96だったので、「勉強時間と点数には強い正の相関がある」と言えそうです。数式を覚える必要はなく、この早見表と照らし合わせるだけで十分です。

相関係数は何を測っているのか

計算式を覚える必要はありませんが、何を見ている数字なのかを一言で言うと、「平均より上か下か」が2つの列で揃っているかどうかです。

勉強時間が平均より長い人は、点数も平均より高い。勉強時間が平均より短い人は、点数も平均より低い。——この「揃い方」がきれいなほど1に近づきます。逆に、勉強時間が平均より長いのに点数は平均以下、という人が混ざるほど、数字は0に近づいていきます。

CAUTION

相関係数が測れるのはまっすぐな関係だけです。「ある点まで増えるが、それを超えると減る」というような山型の関係は、相関係数では0に近い値になり、「関係なし」に見えてしまいます。散布図を先に見るべき理由が、ここにもあります。

外れ値ひとつで、結論がひっくり返る

ここが、この記事でいちばん覚えて帰ってほしいところです。

先ほどのデータに、たった1人を追加してみます。Iさんは9時間勉強したのですが、当日に体調を崩してしまい、45点でした。

Iさん,9,45

これをCSVに1行足して、もう一度同じコードを実行します。

相関係数: 0.0399

0.96だった相関係数が、0.04になりました。早見表で言えば「強い正の相関」から「ほぼ相関なし」への転落です。8人分のデータが示していた傾向が、1人加わっただけで消えてしまいました。

散布図で見ると、何が起きたか分かります

Iさんを追加した散布図。右下に9時間45点の点が1つだけ離れて存在する
Iさん(9時間・45点)を足しただけの散布図です。右下にぽつんと離れた点が1つ増えています。この1点で相関係数が0.96から0.04になりました。数字だけ見ていると気づけませんが、絵にすると一目で分かります。

なぜこんなことが起きるのか

相関係数は、平均から大きく離れた点の影響を強く受けます。Iさんは「勉強時間はいちばん長いのに、点数はいちばん低い」という、それまでの傾向とまったく逆の位置にいます。こうした点は外れ値と呼ばれ、たった1つでも全体の数字を大きく動かします。

どうすればいいのか

大事なのは、外れ値を勝手に消さないことです。都合の悪い点を消せば数字はきれいになりますが、それは分析ではなく都合合わせです。やるべきことは次の順です。

  1. 散布図を見る — ぽつんと離れた点がないかを、目で確認する
  2. その点の事情を調べる — Iさんの場合は「体調不良」という理由があった
  3. 理由を明記したうえで判断する — 「体調不良の1名を除いた8名では0.96」と書けば、隠していることにはならない

入力ミス(点数を450と打っていた等)であれば直せばよいですし、実際に起きたことなら「そういう例もある」という情報として残します。どちらにせよ、まず気づけることが大切です。

CAUTION

「相関係数だけをコードで計算して、そのまま報告書に書く」というのが、いちばん危ない使い方です。数字の裏に、こういう1点が隠れているかもしれません。散布図を先に見るという手順を、必ず守ってください。

3つ以上の項目をまとめて見る

実際のデータには、列が2つだけということはあまりありません。「勉強時間」「睡眠時間」「点数」のように3つ以上ある場合、corr()を列を指定せずに使うと、すべての組み合わせを一度に計算してくれます。

print(df.corr(numeric_only=True).round(2))

ここでは、先ほどのCSVに睡眠時間の列を1つ足したデータで実行してみます(名前,勉強時間,睡眠時間,点数の4列にしたイメージです)。結果は次のような表(相関行列と呼びます)で表示されます。

          勉強時間  睡眠時間    点数
勉強時間      1.00  -0.35  0.96
睡眠時間     -0.35   1.00  -0.21
点数          0.96  -0.21  1.00

表の読み方は簡単です。行と列が交わるところが、その2つの相関係数です。「勉強時間」の行と「点数」の列が交わる場所が0.96、というわけです。

斜めに並ぶ1.00は「自分自身との相関」なので、必ず1になります。ここは見なくて構いません。

INFO

numeric_only=Trueは「数字の列だけを対象にする」という指定です。名前などの文字が入った列があってもエラーにならず、自動的に除いて計算してくれます。

MEMO

列が10個あれば45通りの組み合わせになります。相関行列は「どこから見ればいいか分からない大量の数字」の中から、関係が強そうな組み合わせを見つける最初の一手として使います。目星を付けたら、その2列で散布図を描く、という流れです。

注意点:相関関係は因果関係とは違う

相関分析を使ううえで、必ず知っておきたい注意点があります。それは、「相関がある」ことと「原因と結果の関係(因果関係)がある」ことは、イコールではないという点です。

CAUTION

よく使われる例に「アイスクリームの売上」と「水難事故の件数」があります。この2つには実は正の相関があるのですが、アイスクリームが水難事故の原因というわけではありません。どちらも「気温が高い」という共通の原因によって、一緒に増えているだけなのです。

よくある3つの勘違い

  • 第三の原因がある — 上のアイスクリームの例。裏に「気温」という共通の原因が隠れている
  • 因果が逆 — 「よく売れている店ほど広告費が多い」というデータを見て「広告を出せば売れる」と考える。実際は「売れているから広告費を出せる」のかもしれない
  • たまたま — データが少ないと、意味のない偶然が強い相関に見えることがあります。8人程度のデータで断言しないほうが安全です

今回の「勉強時間と点数」の例でも、相関係数が高かったからといって、「勉強時間さえ増やせば必ず点数が上がる」と単純に言い切ることはできません。もともと成績のよい人が長く勉強しているだけ、という可能性も残ります。

相関分析は、あくまで「関係がありそうだ」という手がかりを見つけるための道具です。そこから先の「なぜそうなるのか」は、データの外側にある事情を調べて考えることになります。

完成版:全部つなげたコード

散布図の保存・相関係数の算出・相関行列の表示までを1つにまとめたものです。CSV_FILEと、比べたい2列の名前を書き換えれば、自分のデータでそのまま使えます。

import pandas as pd
import matplotlib
from matplotlib.figure import Figure
from pathlib import Path

# グラフの日本語が□□□にならないようにする(Windows標準のフォントを指定)
matplotlib.rcParams["font.family"] = "Meiryo"

# ==== ここを自分の内容に書き換える ====
CSV_FILE = Path("study_scores.csv")
X_COLUMN = "勉強時間"
Y_COLUMN = "点数"
OUTPUT_IMAGE = Path("scatter.png")
# ====================================


def load(path):
    if not path.exists():
        print(f"{path} が見つかりません")
        return None
    return pd.read_csv(path, encoding="utf-8-sig")


def draw_scatter(df, path):
    fig = Figure(figsize=(6, 4))
    ax = fig.add_subplot(111)
    ax.scatter(df[X_COLUMN], df[Y_COLUMN], color="#3e8f5c", s=80)
    ax.set_title(f"{X_COLUMN}と{Y_COLUMN}の関係")
    ax.set_xlabel(X_COLUMN)
    ax.set_ylabel(Y_COLUMN)
    fig.tight_layout()
    fig.savefig(path)
    print(f"散布図を {path} に保存しました")


def judge(value):
    v = abs(value)
    if v >= 0.7:
        strength = "強い"
    elif v >= 0.4:
        strength = "中くらいの"
    elif v >= 0.2:
        strength = "弱い"
    else:
        return "ほぼ相関なし"
    direction = "正" if value > 0 else "負"
    return f"{strength}{direction}の相関"


def main():
    df = load(CSV_FILE)
    if df is None:
        return

    print(f"データ件数: {len(df)} 件")
    draw_scatter(df, OUTPUT_IMAGE)

    corr = df[X_COLUMN].corr(df[Y_COLUMN])
    print(f"{X_COLUMN} と {Y_COLUMN} の相関係数: {round(corr, 4)}")
    print(f"判定: {judge(corr)}")

    print("\n--- すべての組み合わせ ---")
    print(df.corr(numeric_only=True).round(2))

    print("\n散布図を必ず目で確認してください。")
    print("ぽつんと離れた点があれば、その1件が結論を変えているかもしれません。")


if __name__ == "__main__":
    main()
INFO

相関係数を出したあとに、早見表と照らして判定を日本語で表示するようにしました。数字だけだと毎回表を見に行くことになりますが、これなら実行結果をそのまま読めます。最後に散布図の確認を促すメッセージも出しています。

ここからどう学びを広げるか

今回学んだ「散布図で見る→相関係数を求める→外れ値を確認する→読み方を知る」という流れは、データ分析でとてもよく使う型です。慣れてきたら、次のようなテーマに進むと学びが広がります。

  • 欠損値の処理:データの一部が空欄になっている場合の扱い方。corr()は空欄を自動で除いて計算しますが、除いた結果が何件になったかは意識しておく必要があります
  • より大きなデータセットの練習:公開されている練習用データで、列が多いときの相関行列の読み方に慣れる
  • 回帰分析:「関係がある」の先にある「では、勉強時間が1時間増えると点数は何点上がるのか」を求める手法
  • 機械学習の入門:相関関係の先にある、予測・分類といった発展的な分野
MEMO

相関分析は、機械学習に進む前の準備運動としても重要です。「どの数字とどの数字が関係していそうか」を先に把握しておくと、この先の分析がぐっとスムーズになります。

うまくいかないときのトラブルシューティング

KeyError: 勉強時間と出ます
CSVの1行目の見出しと、コードで指定した列名が一致していません。見出しの前後に半角スペースが入っていないか確認してください。見た目は合っているのにエラーになる場合は、Excelが付ける目に見えない印(BOM)が原因です。encoding="utf-8-sig"で読み込めば解決します。
相関係数がnanになります
列に数字以外(文字や空欄)が混ざっていて、数値として読み込めていない可能性があります。print(df.dtypes)で各列の種類を確認してください。数字の列なのにobjectと表示されていれば、その列のどこかに文字が入っています。全角の数字やカンマ付きの数値(1,000)もよくある原因です。
相関係数がマイナスになったら、どういう意味ですか?
マイナスの相関係数は「負の相関」を意味します。片方の数字が大きくなるほど、もう片方の数字は小さくなる傾向がある、ということです。たとえば「気温」と「あたたかい飲み物の売上」のような関係が典型例です。強さの読み方は正の場合と同じで、-0.7より小さければ強い負の相関です。
散布図の日本語が□□□になります
matplotlib.rcParams["font.family"] = "Meiryo"の行を書き忘れていないか確認してください。

この行より後でグラフを作る必要があります。順番が逆だと効きません。

Meiryoが入っていない環境では、"MS Gothic""Yu Gothic"でも同じことができます。
データは何件あればいいですか?
明確な決まりはありませんが、件数が少ないほど偶然の影響を受けやすくなります。この記事の8件は説明のための最小限で、実際に何かを判断する材料にするには少なすぎます。数十件あれば、傾向としてある程度の手応えが出てきます。件数が少ないときほど、散布図での確認が重要です。
3つ以上の項目の関係も調べられますか?
はい。df.corr(numeric_only=True)のように列を指定せずに実行すると、すべての数値列どうしの相関係数が一覧(相関行列)で表示されます。詳しくは「3つ以上の項目をまとめて見る」で解説しています。

まとめ

散布図で全体像をつかんでからcorr()で相関係数を求め、早見表で関係の強さを読み取る——という一連の流れを紹介しました。要点を整理します。

  • 数字より先に、絵を見る — 散布図を描いてから相関係数を求める
  • 相関係数は-1から1 — 早見表と照らすだけでよく、数式は不要
  • 外れ値1つで結論は変わる — 0.96が0.04になった例のとおり。消さずに、理由を調べて明記する
  • 列が多いときは相関行列 — まず全体を見て、目星を付けた2列で散布図を描く
  • 相関は因果ではない — 第三の原因、逆の因果、偶然。この3つを疑う

相関分析は、「関係がありそう」という手応えを数字で裏づけるための道具です。断定するための道具ではありません。そのことさえ押さえておけば、手元のデータから言えることの幅が、確実に広がります。前回の基礎編と合わせて、小さな分析を1つずつ積み重ねていきましょう。

データ分析の基礎からおさらいする

平均・合計・グループ集計・グラフ化など、データ分析の基本操作を解説した前回記事です。

基礎編を見る

コメント

タイトルとURLをコピーしました