医療職からデータサイエンティストへ

統計学、機械学習に関する記事をまとめています。

以外に奥深いR,Pythonでの相関行列の計算方法

RやPythonで変数間の相関を見るために、何気なく使う相関行列

実はとても奥深いことを知りました。

今回はRとPythonの算出方法の違いについても触れていきます。(pythonを使ったのはいつぶりだろうか...)

データセットの作成

まずはデータセットを作成します。データは6人の小学生の身長、体重、年齢、50m走のタイムとしましょう。

Rの場合

df <- data_frame(heights=c(110,120,130,130,150,155),
                 weight = c(25,25,25,35,45,50),
                 age = c(6,7,9,10,10,12),
                 time50m =c(15,11,10,10,9,9) )

Pythonの場合

import numpy as np
import pandas as pd

df=pd.DataFrame(np.array([[110,120,130,130,150,155],
             [25,25,25,35,45,50],
             [6,7,9,10,10,12],
             [15,11,10,10,9,9]]).T,columns=["height","weight","age","time50m"])
heights weight age time50m
110 25 6 15
120 25 7 11
130 25 9 10
130 35 10 10
150 45 10 9
155 50 12 9

 相関行列の算出

続いて、RとPythonそれぞれで相関行列を算出してみます。

Rの場合

> cor(df)
           heights     weight        age    time50m
heights  1.0000000  0.9235329  0.9261982 -0.8499811
weight   0.9235329  1.0000000  0.8601935 -0.6511951
age      0.9261982  0.8601935  1.0000000 -0.8516625
time50m -0.8499811 -0.6511951 -0.8516625  1.0000000

Pythonの場合

df.corr()
height weight age time50m
height 1.000000 0.923533 0.926198 -0.849981
weight 0.923533 1.000000 0.860194 -0.651195
age 0.926198 0.860194 1.000000 -0.851662
time50m -0.849981 -0.651195 -0.851662 1.000000

身長、体重、年齢はそれぞれ正の相関、50mタイムは負の相関です。これは、直感的に分かりやすいですね。 RもPythonも結果は同じになりました

それでは、データに欠損を作ってみましょう。

Rの場合

df$weight[c(5,6)] <- NA
df$time50m[1] <- NA

Pythonの場合

df.loc[[4,5],"weight"] = np.nan
df.loc[0,"time50m"] = np.nan
heights weight age time50m
110 25 6 NA
120 25 7 11
130 25 9 10
130 35 10 10
150 NA 10 9
155 NA 12 9

これで相関行列を出してみると

R

> cor(df)
          heights weight       age time50m
heights 1.0000000     NA 0.9574271      NA
weight         NA      1        NA      NA
age     0.9574271     NA 1.0000000      NA
time50m        NA     NA        NA       1

Python

df.corr()
height weight age time50m
height 1.000000 0.923533 0.926198 -0.849981
weight 0.923533 1.000000 0.860194 -0.651195
age 0.926198 0.860194 1.000000 -0.851662
time50m -0.849981 -0.651195 -0.851662 1.000000

おや、どうやら結果が違うようです。

Rのcor関数について

これは、欠損値の扱い方がRとPythonでは違うために起こります。Rでは欠損を除いて関数を適応する際にna.rm=Tと指定することがあるかと思います。(meanなど)

しかし、cor関数にはna.rmが指定できず、代わりにuseという引数を使って、欠損の扱いを指定できます。

useには

  • everything(デフォルト)
  • all.obs
  • complete.obs
  • pairwise.complete.obs

が指定できます。

試しにやってみましょう。

R

>#everthing
> cor(df,use = "everything")
          heights weight       age time50m
heights 1.0000000     NA 0.9261982      NA
weight         NA      1        NA      NA
age     0.9261982     NA 1.0000000      NA
time50m        NA     NA        NA       1
> 
> #all.obs
> cor(df,use = "all.obs")
 cor(df, use = "all.obs") でエラー: 
   cov/cor 関数に欠損した観測値があります 
> 
> #complete.obs
> cor(df,use = "complete.obs")
           heights     weight        age    time50m
heights  1.0000000  0.5000000  0.9449112 -1.0000000
weight   0.5000000  1.0000000  0.7559289 -0.5000000
age      0.9449112  0.7559289  1.0000000 -0.9449112
time50m -1.0000000 -0.5000000 -0.9449112  1.0000000
> 
> #pairwise.complete.obs
> cor(df,use="pairwise.complete.obs")
           heights     weight        age    time50m
heights  1.0000000  0.5222330  0.9261982 -0.9669876
weight   0.5222330  1.0000000  0.7302967 -0.5000000
age      0.9261982  0.7302967  1.0000000 -0.8882348
time50m -0.9669876 -0.5000000 -0.8882348  1.0000000

順番にいきましょう。まず、デフォルトのeverythingですが、これは変数に欠損が一つでもあれば、その変数とその他の変数との相関係数を算出しません。

all.obsはさらに厳しくて、全てのデータが完璧に揃っていた場合のみ相関行列を算出します。

complete.obsをpairwise.complete.obsが少しややこしいですが、下の図をご覧ください。

complete.obsは、欠損のある行を全て除いて相関係数を算出しているのに対して、pairwise.complete.obsは欠損のある変数との相関係数のみcomplete.obsを使って相関係数を算出しています。

f:id:h-wadsworth02:20190213235648p:plain

例えば、weightとtime50mの時は真ん中3人分だけが使われているため、complete.obsと同じ結果になっています。逆にheightとageは欠損がないので、everytingと同様の結果になっています。

Pythonの場合はpairwise.complete.obsと同じことを行なっています。

ちなみにRでは共分散を算出するcov関数でも同様の引数を指定できます!

相関行列の注意点

ここでpairwise.complete.obsの相関係数、もしくはpythonの相関係数に注目してみてください。本来、0.92と高い相関にあったheightとweightの相関係数が0.52と小さな相関になっています。 これは本来相関関係にあるはずのペアデータが欠損しているためです。

大量のデータかつ完全ランダムな欠損であれば、pairwise.complete.obsやpythonのデフォルトでも問題ありませんが、欠損に規則性があった場合、(例えば高学年は体重を測定していないとか)重要な相関関係を見逃す可能性もあります。

普段何気なく使っている相関行列も欠損がどのように処理をされているか知っておいて損はないですね!