実験やデータ分析で「どの条件が結果に大きく影響しているのか判断できない」「各条件の影響度をうまく説明できない」と悩むことはありますよね?そんなときに最初の手がかりになるのが相関係数です。
本記事では、pythonで相関係数を求めて可視化する方法を紹介します。計算にはpandasのcorr()(相関行列をまとめて求める)とscipyのpearsonr()(相関係数とp値を同時に求める)を、可視化にはseabornのヒートマップ(相関行列を色で見渡す)を使い、scikit-learnに付属する実データ(糖尿病データセット)で解説します。
この記事を読むことで、pythonで相関係数を求める方法と、結果から影響の大きい変数や似た変数を読み取る手順が分かります。
pythonで相関係数を求めて可視化する方法
pythonで相関係数を求めるにはpandasとscipy、結果を可視化するにはseabornを使います。
- pandas.corr()(計算):データフレームの全列の組み合わせについて、相関係数をまとめて求める
- scipy.stats.pearsonr()(計算):2つの変数の相関係数と、p値を同時に求める
- seaborn.heatmap()(可視化):相関行列をヒートマップにして、色で全体を見渡す
計算と可視化は「どちらか一方を選ぶ」ものではなく、組み合わせて使うと効果的です。本記事では、目的変数との相関をpandasで求め、scipyでp値を確認し、seabornで全体を見渡したうえで、説明変数同士の似たペアを探して変数を絞り込むところまで進めます。全体の流れは次の図のとおりです。

環境準備とデータ読み込み|load_diabetes
今回は、scikit-learnに付属する糖尿病のデータセット(load_diabetes)を使います。年齢(age)、性別(sex)、BMI(bmi)、血圧(bp)、血液検査の値(s1〜s6)の10個の説明変数と、病気の進行度を表す目的変数(target)が入った公開データです。
「どの条件が結果に効いているか」を探すという点で、実験条件と測定結果の関係を調べる場面とよく似た構造になっています。
データの操作に使うpandasと、データセットを読み込むload_diabetesをインポートします。
import pandas as pd
from sklearn.datasets import load_diabetes
as_frame=Trueを指定すると、データをpandasのデータフレームとして受け取れます。.frameで説明変数と目的変数(target)が1つにまとまった表を取り出し、head()で先頭5行を確認します。
df = load_diabetes(as_frame=True).frame
df.head()
出力:
| age | sex | bmi | bp | s1 | s2 | s3 | s4 | s5 | s6 | target | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0.038076 | 0.050680 | 0.061696 | 0.021872 | -0.044223 | -0.034821 | -0.043401 | -0.002592 | 0.019907 | -0.017646 | 151.0 |
| 1 | -0.001882 | -0.044642 | -0.051474 | -0.026328 | -0.008449 | -0.019163 | 0.074412 | -0.039493 | -0.068332 | -0.092204 | 75.0 |
| 2 | 0.085299 | 0.050680 | 0.044451 | -0.005670 | -0.045599 | -0.034194 | -0.032356 | -0.002592 | 0.002861 | -0.025930 | 141.0 |
| 3 | -0.089063 | -0.044642 | -0.011595 | -0.036656 | 0.012191 | 0.024991 | -0.036038 | 0.034309 | 0.022688 | -0.009362 | 206.0 |
| 4 | 0.005383 | -0.044642 | -0.036385 | 0.021872 | 0.003935 | 0.015596 | 0.008142 | -0.002592 | -0.031988 | -0.046641 | 135.0 |
shapeで行数と列数を確認します。
df.shape
出力:
(442, 11)
行数は442、列数は11(説明変数10個と target)です。
describe()で、列ごとの平均や標準偏差などをまとめて確認します。
df.describe()
出力:
| age | sex | bmi | bp | s1 | s2 | s3 | s4 | s5 | s6 | target | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| count | 4.420000e+02 | 4.420000e+02 | 4.420000e+02 | 4.420000e+02 | 4.420000e+02 | 4.420000e+02 | 4.420000e+02 | 4.420000e+02 | 4.420000e+02 | 4.420000e+02 | 442.000000 |
| mean | -2.511817e-19 | 1.230790e-17 | -2.245564e-16 | -4.797570e-17 | -1.381499e-17 | 3.918434e-17 | -5.777179e-18 | -9.042540e-18 | 9.293722e-17 | 1.130318e-17 | 152.133484 |
| std | 4.761905e-02 | 4.761905e-02 | 4.761905e-02 | 4.761905e-02 | 4.761905e-02 | 4.761905e-02 | 4.761905e-02 | 4.761905e-02 | 4.761905e-02 | 4.761905e-02 | 77.093005 |
| min | -1.072256e-01 | -4.464164e-02 | -9.027530e-02 | -1.123988e-01 | -1.267807e-01 | -1.156131e-01 | -1.023071e-01 | -7.639450e-02 | -1.260971e-01 | -1.377672e-01 | 25.000000 |
| 25% | -3.729927e-02 | -4.464164e-02 | -3.422907e-02 | -3.665608e-02 | -3.424784e-02 | -3.035840e-02 | -3.511716e-02 | -3.949338e-02 | -3.324559e-02 | -3.317903e-02 | 87.000000 |
| 50% | 5.383060e-03 | -4.464164e-02 | -7.283766e-03 | -5.670422e-03 | -4.320866e-03 | -3.819065e-03 | -6.584468e-03 | -2.592262e-03 | -1.947171e-03 | -1.077698e-03 | 140.500000 |
| 75% | 3.807591e-02 | 5.068012e-02 | 3.124802e-02 | 3.564379e-02 | 2.835801e-02 | 2.984439e-02 | 2.931150e-02 | 3.430886e-02 | 3.243232e-02 | 2.791705e-02 | 211.500000 |
| max | 1.107267e-01 | 5.068012e-02 | 1.705552e-01 | 1.320436e-01 | 1.539137e-01 | 1.987880e-01 | 1.811791e-01 | 1.852344e-01 | 1.335973e-01 | 1.356118e-01 | 346.000000 |
describe()の見どころ
age や bmi など説明変数の平均がほぼ0で、標準偏差がどれもほぼ同じ値(約0.048)になっています。load_diabetesの説明変数は、あらかじめ標準化(平均0にそろえてスケール調整)された状態で入っているためです。そのため、年齢が0.05のような不思議な値に見えます。一方、target だけは元のスケール(25〜346程度)のままです。
説明変数と target でスケールがまったく違いますが、相関係数を求めるうえでは問題ありません。
相関係数は単位やスケールの影響を受けないので、標準化された説明変数と元のスケールの target をそのまま比べられます。
実務の実験データでも、温度(℃)、時間(分)、濃度(%)のように単位がばらばらな条件を並べることがよくあります。相関係数なら、単位をそろえる前処理をしなくても関係の強さを同じ物差しで比べられるのが利点です。
相関係数をまとめて求める|pandas.corr()
まずはpandasのcorr()で、すべての列の組み合わせについて相関係数を求めます。データフレームに対して呼ぶだけで、相関行列(縦横に変数を並べた表)が返ってきます。特に指定しなければ、ピアソンの相関係数が計算されます。
df.corr()で、11列すべての組み合わせの相関係数を求めます。
df.corr()
出力:
| age | sex | bmi | bp | s1 | s2 | s3 | s4 | s5 | s6 | target | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| age | 1.000000 | 0.173737 | 0.185085 | 0.335428 | 0.260061 | 0.219243 | -0.075181 | 0.203841 | 0.270774 | 0.301731 | 0.187889 |
| sex | 0.173737 | 1.000000 | 0.088161 | 0.241010 | 0.035277 | 0.142637 | -0.379090 | 0.332115 | 0.149916 | 0.208133 | 0.043062 |
| bmi | 0.185085 | 0.088161 | 1.000000 | 0.395411 | 0.249777 | 0.261170 | -0.366811 | 0.413807 | 0.446157 | 0.388680 | 0.586450 |
| bp | 0.335428 | 0.241010 | 0.395411 | 1.000000 | 0.242464 | 0.185548 | -0.178762 | 0.257650 | 0.393480 | 0.390430 | 0.441482 |
| s1 | 0.260061 | 0.035277 | 0.249777 | 0.242464 | 1.000000 | 0.896663 | 0.051519 | 0.542207 | 0.515503 | 0.325717 | 0.212022 |
| s2 | 0.219243 | 0.142637 | 0.261170 | 0.185548 | 0.896663 | 1.000000 | -0.196455 | 0.659817 | 0.318357 | 0.290600 | 0.174054 |
| s3 | -0.075181 | -0.379090 | -0.366811 | -0.178762 | 0.051519 | -0.196455 | 1.000000 | -0.738493 | -0.398577 | -0.273697 | -0.394789 |
| s4 | 0.203841 | 0.332115 | 0.413807 | 0.257650 | 0.542207 | 0.659817 | -0.738493 | 1.000000 | 0.617859 | 0.417212 | 0.430453 |
| s5 | 0.270774 | 0.149916 | 0.446157 | 0.393480 | 0.515503 | 0.318357 | -0.398577 | 0.617859 | 1.000000 | 0.464669 | 0.565883 |
| s6 | 0.301731 | 0.208133 | 0.388680 | 0.390430 | 0.325717 | 0.290600 | -0.273697 | 0.417212 | 0.464669 | 1.000000 | 0.382483 |
| target | 0.187889 | 0.043062 | 0.586450 | 0.441482 | 0.212022 | 0.174054 | -0.394789 | 0.430453 | 0.565883 | 0.382483 | 1.000000 |
対角線(自分自身との相関)はすべて1.000000になり、表は対角線を挟んで対称になります。
このあと何度も使うので、相関行列をcorr_matrixに入れておきます。
corr_matrix = df.corr()
知りたいのは「どの説明変数が target と関係しているか」なので、target の列だけを取り出します。
corr_matrix['target']
出力:
| target | |
|---|---|
| age | 0.187889 |
| sex | 0.043062 |
| bmi | 0.586450 |
| bp | 0.441482 |
| s1 | 0.212022 |
| s2 | 0.174054 |
| s3 | -0.394789 |
| s4 | 0.430453 |
| s5 | 0.565883 |
| s6 | 0.382483 |
| target | 1.000000 |
変数s3が負の相関で、それ以外は正の相関です。s3は値が大きいほど target が小さくなる傾向がある、ということです。
関係の強さは、正負に関係なく絶対値で比べます。abs()で絶対値にしてから、sort_values(ascending=False)で大きい順に並べます。
corr_matrix['target'].abs().sort_values(ascending=False)
出力:
| target | |
|---|---|
| target | 1.000000 |
| bmi | 0.586450 |
| s5 | 0.565883 |
| bp | 0.441482 |
| s4 | 0.430453 |
| s3 | 0.394789 |
| s6 | 0.382483 |
| s1 | 0.212022 |
| age | 0.187889 |
| s2 | 0.174054 |
| sex | 0.043062 |
変数bmiが最も目的変数と相関が強そうです。
pandas.corr()の使いどころと注意点
corr()の強みは、1行で全組み合わせの相関がまとめて出ることです。最初に「どの変数が効いていそうか」を当たりをつける段階に向いています。
一方で、つまずきやすい点もあります。
abs()を付けずに並べると、s3(-0.394789)のような負の相関が下位に沈んでしまいます。強さを比べるときは絶対値、向きを確認するときは元の値、と使い分けます。- 並べ替えた結果の先頭には、target 自身(1.000000)が必ず来ます。説明変数の順位を見るときは、2行目から読みます。
corr()が返すのは相関係数だけで、その相関が偶然でないかどうか(p値)は分かりません。これを確認するのが次のscipyです。
相関係数とp値を求める|scipy.stats.pearsonr()
scipyのstats.pearsonr()を使うと、2つの変数の相関係数と、p値を同時に求められます。
scipyから統計関数をまとめたstatsをインポートします。
from scipy import stats
まずは、最も相関が強かった bmi と target で試します。
stats.pearsonr(df['bmi'], df['target'])
出力:
PearsonRResult(statistic=np.float64(0.5864501344746887), pvalue=np.float64(3.4660064451669974e-42))
statisticが相関係数、pvalueがp値です。相関係数0.5864501344746887は、corr()で求めた bmi の値(0.586450)と一致しています。
pearsonr()は2変数ずつしか計算できないので、for文で target 以外の列を順番に回し、結果をリストにためてからデータフレームにします。pearsonr()の結果はcorrelation, p_valueのように2つの変数で受け取れます。
data_list = []
for col in df.columns:
if col != 'target':
correlation, p_value = stats.pearsonr(df['target'], df[col])
data_list.append({'変数名':col, '相関係数':correlation, 'p値':p_value})
pearson_df = pd.DataFrame(data_list)
pearson_df
出力:
| 変数名 | 相関係数 | p値 | |
|---|---|---|---|
| 0 | age | 0.187889 | 7.055686e-05 |
| 1 | sex | 0.043062 | 3.664293e-01 |
| 2 | bmi | 0.586450 | 3.466006e-42 |
| 3 | bp | 0.441482 | 1.649372e-22 |
| 4 | s1 | 0.212022 | 6.920712e-06 |
| 5 | s2 | 0.174054 | 2.359848e-04 |
| 6 | s3 | -0.394789 | 6.162865e-18 |
| 7 | s4 | 0.430453 | 2.304253e-21 |
| 8 | s5 | 0.565883 | 8.826459e-39 |
| 9 | s6 | 0.382483 | 7.580083e-17 |
相関係数の列だけを取り出し、絶対値の大きい順に並べます。
pearson_df['相関係数'].abs().sort_values(ascending=False)
出力:
| 相関係数 | |
|---|---|
| 2 | 0.586450 |
| 8 | 0.565883 |
| 3 | 0.441482 |
| 7 | 0.430453 |
| 6 | 0.394789 |
| 9 | 0.382483 |
| 4 | 0.212022 |
| 0 | 0.187889 |
| 5 | 0.174054 |
| 1 | 0.043062 |
列を1つだけ取り出したため、変数名が消えて行番号(インデックス)だけになっています。2が bmi、8が s5 …と元の表と照らし合わせる必要があり、少し読みにくい形です。
条件を指定して、p値が0.05より大きい行を取り出します。
pearson_df[pearson_df['p値']>0.05]
出力:
| 変数名 | 相関係数 | p値 | |
|---|---|---|---|
| 1 | sex | 0.043062 | 0.366429 |
p値が0.05を超えたのは sex だけでした。sex は相関係数も0.043062と最も小さく、target との相関がゼロでないとは言えない変数です。
変数名が消えないように、絶対値を新しい列として追加します。
pearson_df['絶対値'] = pearson_df['相関係数'].abs()
pearson_df
出力:
| 変数名 | 相関係数 | p値 | 絶対値 | |
|---|---|---|---|---|
| 0 | age | 0.187889 | 7.055686e-05 | 0.187889 |
| 1 | sex | 0.043062 | 3.664293e-01 | 0.043062 |
| 2 | bmi | 0.586450 | 3.466006e-42 | 0.586450 |
| 3 | bp | 0.441482 | 1.649372e-22 | 0.441482 |
| 4 | s1 | 0.212022 | 6.920712e-06 | 0.212022 |
| 5 | s2 | 0.174054 | 2.359848e-04 | 0.174054 |
| 6 | s3 | -0.394789 | 6.162865e-18 | 0.394789 |
| 7 | s4 | 0.430453 | 2.304253e-21 | 0.430453 |
| 8 | s5 | 0.565883 | 8.826459e-39 | 0.565883 |
| 9 | s6 | 0.382483 | 7.580083e-17 | 0.382483 |
追加した「絶対値」の列で並べ替え、reset_index(drop=True)で行番号を振り直します。
pearson_df.sort_values("絶対値", ascending=False).reset_index(drop=True)
出力:
| 変数名 | 相関係数 | p値 | 絶対値 | |
|---|---|---|---|---|
| 0 | bmi | 0.586450 | 3.466006e-42 | 0.586450 |
| 1 | s5 | 0.565883 | 8.826459e-39 | 0.565883 |
| 2 | bp | 0.441482 | 1.649372e-22 | 0.441482 |
| 3 | s4 | 0.430453 | 2.304253e-21 | 0.430453 |
| 4 | s3 | -0.394789 | 6.162865e-18 | 0.394789 |
| 5 | s6 | 0.382483 | 7.580083e-17 | 0.382483 |
| 6 | s1 | 0.212022 | 6.920712e-06 | 0.212022 |
| 7 | age | 0.187889 | 7.055686e-05 | 0.187889 |
| 8 | s2 | 0.174054 | 2.359848e-04 | 0.174054 |
| 9 | sex | 0.043062 | 3.664293e-01 | 0.043062 |
変数名、相関係数(向き)、p値、絶対値(強さ)が1つの表にそろい、そのまま報告資料に貼れる形になりました。
p値と相関係数の違い
p値と相関係数は、見ているものが違います。
p値は「相関がゼロでないと言えるか」(偶然と考えにくいか)を、相関係数は「相関の強弱と向き」を示します。
たとえば今回の s2 は、相関係数が0.174054と小さめですが、p値は2.359848e-04で0.05を大きく下回っています。「相関がゼロではなさそう」ということと「相関が強い」ということは別の話です。p値が小さいからといって影響が大きいとは限らないので、まず相関係数で強さを見て、p値は偶然の可能性を除くための確認に使う、という順番で読むと混乱しません。
実務で上司や共同研究者に「この条件は効いているのか」と聞かれたとき、相関係数だけでなくp値も添えておくと、「たまたまそう見えているだけでは?」という質問に答えやすくなります。
相関行列を色で見渡す|seaborn.heatmap()
数値の表は、変数が10個を超えるとどこに注目すればよいか分かりにくくなります。そこで、seabornのheatmap()で相関行列を色付きの図にします。
描画に使うseabornとmatplotlibをインポートします。
import seaborn as sns
import matplotlib.pyplot as plt
pandasで求めたcorr_matrixをそのまま渡します。annot=Trueでマスの中に数値を表示し、fmt='.2f'で小数点以下2桁にそろえます。cmap='coolwarm'は正の相関を赤、負の相関を青で表す配色で、vmin=-1, vmax=1で色の範囲を-1〜1に固定しています。
plt.figure(figsize=(10, 8))
sns.heatmap(corr_matrix, annot=True,
fmt='.2f', cmap='coolwarm',
vmin=-1, vmax=1)
plt.show()
出力:

ヒートマップの読み方
色の範囲を-1〜1に固定すると、中央の白が相関0に対応し、赤と青の濃さを同じ基準で読み取れます。この指定をしないと、データ内の最小値と最大値に合わせて色が割り当てられるため、弱い相関でも濃い色に見えてしまうことがあります。複数のデータセットのヒートマップを並べて比べるときにも、範囲を固定しておくと見比べやすくなります。
図で最も目立つのは s1-s2(0.90)のマスです。これは今回のデータでほぼ同じ情報を持つ変数のペアです。また、s3-s4(-0.74)の濃い青も目を引きます。
ここで気づくのが、target との相関だけを見ていては分からないことがあるという点です。pandasとscipyでは target の列だけに注目していましたが、ヒートマップでは説明変数同士の関係も一度に見えます。次の章では、この「説明変数同士の相関」を数値で取り出してみます。
実データで試す:似ている説明変数のペアを探す
ヒートマップで目立った s1-s2 のような「似た変数のペア」を、目で探すのではなくコードで一覧にします。説明変数が数十個になると、ヒートマップだけでは見落としが出るためです。
似た変数が両方入っていると、機械学習のモデルを作るときに同じ情報を二重に入れることになり、どちらの変数が効いているのかが解釈しにくくなります(多重共線性と呼ばれる問題です)。変数を絞り込む前の下調べとして、似たペアを洗い出しておきます。
説明変数同士の相関だけを見たいので、drop()で target の行と列を取り除きます。
drop_target_corr = corr_matrix.drop(index='target', columns='target')
print(drop_target_corr)
出力:
age sex bmi bp s1 s2 s3 \
age 1.000000 0.173737 0.185085 0.335428 0.260061 0.219243 -0.075181
sex 0.173737 1.000000 0.088161 0.241010 0.035277 0.142637 -0.379090
bmi 0.185085 0.088161 1.000000 0.395411 0.249777 0.261170 -0.366811
bp 0.335428 0.241010 0.395411 1.000000 0.242464 0.185548 -0.178762
s1 0.260061 0.035277 0.249777 0.242464 1.000000 0.896663 0.051519
s2 0.219243 0.142637 0.261170 0.185548 0.896663 1.000000 -0.196455
s3 -0.075181 -0.379090 -0.366811 -0.178762 0.051519 -0.196455 1.000000
s4 0.203841 0.332115 0.413807 0.257650 0.542207 0.659817 -0.738493
s5 0.270774 0.149916 0.446157 0.393480 0.515503 0.318357 -0.398577
s6 0.301731 0.208133 0.388680 0.390430 0.325717 0.290600 -0.273697
s4 s5 s6
age 0.203841 0.270774 0.301731
sex 0.332115 0.149916 0.208133
bmi 0.413807 0.446157 0.388680
bp 0.257650 0.393480 0.390430
s1 0.542207 0.515503 0.325717
s2 0.659817 0.318357 0.290600
s3 -0.738493 -0.398577 -0.273697
s4 1.000000 0.617859 0.417212
s5 0.617859 1.000000 0.464669
s6 0.417212 0.464669 1.000000
重複を消すためのマスク(目隠し)を作るのにnumpyを使います。
import numpy as np
相関行列は対角線を挟んで対称なので、同じペアが2回ずつ出てきます(age-sex と sex-age など)。また、対角線は自分自身との相関で必ず1になります。np.tril()で対角線を含む下三角をTrueにしたマスクを作り、これらを取り除く準備をします。
mask = np.tril(np.ones(drop_target_corr.shape), k=0).astype(bool)
mask
出力:
array([[ True, False, False, False, False, False, False, False, False,
False],
[ True, True, False, False, False, False, False, False, False,
False],
[ True, True, True, False, False, False, False, False, False,
False],
[ True, True, True, True, False, False, False, False, False,
False],
[ True, True, True, True, True, False, False, False, False,
False],
[ True, True, True, True, True, True, False, False, False,
False],
[ True, True, True, True, True, True, True, False, False,
False],
[ True, True, True, True, True, True, True, True, False,
False],
[ True, True, True, True, True, True, True, True, True,
False],
[ True, True, True, True, True, True, True, True, True,
True]])
where()は条件がTrueの場所だけ値を残し、それ以外を欠損値にします。~maskでマスクを反転させているので、対角線より上(上三角)だけが残ります。
upper_corr = drop_target_corr.where(~mask)
stack()で行列を「変数A、変数B、相関係数」の縦長の表に変換します。欠損値になった下三角は、このとき取り除かれます。
stacked_corr = upper_corr.stack().reset_index()
stacked_corr.columns = ['variable_A', 'variable_B', 'correlation']
print(stacked_corr)
出力:
variable_A variable_B correlation 0 age sex 0.173737 1 age bmi 0.185085 2 age bp 0.335428 3 age s1 0.260061 4 age s2 0.219243 (中略) 30 s1 s2 0.896663 31 s1 s3 0.051519 32 s1 s4 0.542207 33 s1 s5 0.515503 34 s1 s6 0.325717 35 s2 s3 -0.196455 36 s2 s4 0.659817 37 s2 s5 0.318357 38 s2 s6 0.290600 39 s3 s4 -0.738493 40 s3 s5 -0.398577 41 s3 s6 -0.273697 42 s4 s5 0.617859 43 s4 s6 0.417212 44 s5 s6 0.464669
10個の説明変数から2つを選ぶ組み合わせ、45ペア(0〜44)が重複なく並びました。
負の相関も「似ている」に含めたいので、絶対値の列を追加してから大きい順に並べ、上位5ペアを表示します。
stacked_corr['abs_corr'] = stacked_corr['correlation'].abs()
top_pairs = stacked_corr.sort_values(by='abs_corr', ascending=False)
top_pairs.head(5).reset_index(drop=True)
出力:
| variable_A | variable_B | correlation | abs_corr | |
|---|---|---|---|---|
| 0 | s1 | s2 | 0.896663 | 0.896663 |
| 1 | s3 | s4 | -0.738493 | 0.738493 |
| 2 | s2 | s4 | 0.659817 | 0.659817 |
| 3 | s4 | s5 | 0.617859 | 0.617859 |
| 4 | s1 | s4 | 0.542207 | 0.542207 |
結果の読み取りと変数の絞り方
ここまでの結果を整理すると、次のようになります。
- 関係が強そうな変数:target との相関は bmi(0.59)、s5(0.57)、bp(0.44)が上位
- 似た変数:s1-s2(0.90)は特に近く、s3-s4(-0.74)は向きが逆でも情報が重なる
- 相関がゼロと言えない変数:p値が0.05を超えたのは sex だけ
s3-s4 は相関係数がマイナスですが、「片方が大きいともう片方が小さい」という関係がはっきりしているので、一方の値からもう一方をある程度推測できます。向きが逆でも、情報が重なっている点は s1-s2 と同じです。
似たペアは、target との相関が高いほうを残すのが基本です。たとえば s1 と s2 なら、target との相関は s1 が0.212022、s2 が0.174054なので、s1 を残します。
ただし、相関だけで決めず、落としても精度が変わらないかを確認します。相関係数は2つの変数の直線的な関係しか見ていないため、他の変数と組み合わせたときにどう働くかまでは分かりません。実際にモデルを作り、変数を外す前と後で精度を比べてから判断するのが安全です。
- pandas.corr()で target との相関を求め、絶対値で並べる
- scipy.stats.pearsonr()でp値を確認し、相関がゼロと言えない変数を把握する
- seaborn.heatmap()で全体を見渡し、説明変数同士の似たペアに気づく
- 上三角を取り出して似たペアを一覧にし、target との相関が高いほうを残す
- 変数を落としても精度が変わらないかを確認する
計算(pandas・scipy)と可視化(seaborn)の比較
計算に使うpandas・scipyと、可視化に使うseabornを、実務での使いどころの観点で比べます。
| 方法 | 得られるもの | 向いている場面 | 注意点 |
|---|---|---|---|
| pandas.corr() | 全組み合わせの相関係数(相関行列) | 最初に全体の当たりをつける | p値は出ない |
| scipy.stats.pearsonr() | 2変数の相関係数とp値 | 偶然でないかを確認し、報告用の表を作る | 2変数ずつなので、全変数を調べるにはfor文が必要 |
| seaborn.heatmap() | 相関行列の図 | 説明変数同士も含めて全体を見渡す、人に説明する | 色の範囲を-1〜1に固定しないと濃さを比べにくい |
pandasとseabornは、corr_matrixを作れば続けて使えるので相性がよい組み合わせです。scipyはコードが少し長くなりますが、「その相関は偶然では?」に答える材料が手に入ります。
実際に試して感じたこと
実際にコードを動かしてみて、筆者が感じたことをまとめます。
- つまずいたところ、出たエラー: データの処理につまずきました。for文が難しかったです。
- 意外だったこと: 目的変数との相関の他に、説明変数同士の相関も見て考える必要があると気づきました。
- 実務ならどの方法を使うか: seaborn
- 初心者へのひとこと: データを可視化することで気づくことがあります。相関係数が高いからといって、必ずしも要因であるとは限らないので注意してください。
for文でつまずきやすいのは、scipy.stats.pearsonr()で全変数の相関係数とp値をまとめて計算するSTEPです。target 自身を除く条件(if col != 'target')、結果を辞書にしてリストにためる処理、最後にデータフレームにする処理が一度に出てくるので、つまずいたときは1行ずつprint()しながら確かめると理解しやすくなります。
説明変数同士の相関は、ヒートマップの章で s1-s2(0.90)や s3-s4(-0.74)として目立っていた関係です。pandasやscipyで target の列だけを数値で見ていると、この関係は見えません。
まとめ
本記事では、pythonで相関係数をpandasとscipyで求め、seabornで可視化する方法を紹介しました。
- pandas.corr()(計算):1行で相関行列を求められる。
abs()とsort_values()で target との相関の強い順に並べられる - scipy.stats.pearsonr()(計算):相関係数とp値を同時に求められる。for文で全変数をまとめて計算し、表にできる
- seaborn.heatmap()(可視化):相関行列を色で見渡せる。説明変数同士の似たペアにも気づける
おすすめは seaborn.heatmap() です。
corr()で相関行列を作るところまではpandasの1行で済み、そこからheatmap()に渡すだけで、target との関係と説明変数同士の関係を一度に見渡せます。今回のデータでも、ヒートマップで最も目立つのは s1-s2 の強い相関でした。数値で詳しく確かめたいときや、報告用にp値を添えたいときはscipyを組み合わせてください。
ただし、相関係数が高いからといって、その変数が必ずしも要因であるとは限りません。相関係数は「どの条件から詳しく調べるか」の優先順位を決める道具として使い、最終的な判断は、変数を落としたときの精度の変化や、実験・現場の知見と合わせて行うのがおすすめです。
よくある質問
- pythonで相関係数を求める一番簡単な方法は?
-
pandasのデータフレームに対してcorr()を呼ぶのが一番簡単です。全列の組み合わせの相関係数が相関行列としてまとめて返ってきます。目的変数との相関だけを見たいときは、corr_matrix['target']のように列を取り出します。
- 相関係数とp値はどう使い分ければよいですか?
-
相関係数は「相関の強弱と向き」、p値は「相関がゼロでないと言えるか」を示します。まず相関係数で強さを見て、p値で偶然の可能性を除く、という順番で読むと混乱しません。p値はscipy.stats.pearsonr()で求められます。
- 負の相関はどう扱えばよいですか?
-
関係の強さを比べるときは、abs()で絶対値にしてから並べます。そのまま並べると、強い負の相関が下位に沈んでしまいます。向きを確認したいときは、元の値も合わせて見てください。
- ヒートマップでvmin=-1, vmax=1を指定するのはなぜですか?
-
色の範囲を-1〜1に固定すると、中央の白が相関0に対応し、赤と青の濃さを同じ基準で読み取れるからです。指定しないと、データ内の最小値と最大値に合わせて色が割り当てられます。
- 相関係数が高い変数が原因だと考えてよいですか?
-
相関係数が高いからといって、必ずしも要因であるとは限りません。また、似た説明変数のペアがある場合は、target との相関が高いほうを残したうえで、変数を落としても精度が変わらないかを確認することが大切です。

コメント