ラベル 分散 の投稿を表示しています。 すべての投稿を表示
ラベル 分散 の投稿を表示しています。 すべての投稿を表示

2012/09/19

文系のための「二変数の関係」(3)

これまでの話から、二つの変数間の関係を見るための方法には、
相関係数以外にも単回帰という方法があることが理解できたハズ。

単回帰は、相関係数に良く似ているが、
一方の変数によってもう一方を説明するという考え方に基づいている。
相関係数は、二つの変数を平等に見ていたので、この点が大きく異なる。

また、重要なこととして、相関係数は二つの変数が平等なので軸は関係無いが、
単回帰の場合は、どちらを説明変数に置くかによって値が異なる。
まぁ、この辺りは、感覚的に理解できていることだろうと信じたい。

さて、今回は、もう少し単回帰について踏み込んでみる。
実は、前回の話では、二つの変数間の関係の強さについては、
十分に検討ができていなかったのである。

相関係数と同様に、二つの変数間の関係の強さを観察するには、
推定された値と実際の値との誤差の部分の解釈が必要となる。
本日は、このことについて整理する。

ということで、前回のデータの読み込みから。

# Windows と Linux の人は次のコマンド
X <- read.table("clipboard", sep=",", header=TRUE)

# Mac の人は次のコマンド
X <- read.table(pipe("pbpaste") , sep=",", header=TRUE)

以下が、そのデータ。データの説明に関しては、平均の話を参照。

Oroshi_Kg, Taka, Naka, Yasu
Tai, 1336, 3150, 1217, 53
Chinu, 226, 630, 513, 105
Sawara, 212, 1575, 1259, 840
Yazu, 4834, 840, 315, 179
Suzuki, 618, 1575, 1146, 525
Akou, 21, 4725, 3129, 1575
Kochi, 28, 2100, 874, 210
Okoze, 28, 5250, 2635, 210
Ainame, 29, 3150, 621, 315
Hage, 1205, 1890, 383, 210
Konoshiro, 21, 2100, 1100, 105
Sayori, 12, 5250, 3916, 1260
Anago, 1637, 2625, 1721, 630
Mebaru, 330, 4200, 1680, 315
Tachiuo, 1211, 2310, 930, 105
Hamo, 1622, 3938, 592, 53
Karei, 41, 6300, 3178, 525
Hirame, 540, 2100, 1496, 210
Aji, 5149, 3150, 789, 210
Saba, 5413, 3675, 625, 53
Ika, 2414, 2888, 1083, 105
Tako, 1844, 1890, 1180, 525
Ebi, 560, 7350, 1420, 525
KurumaEbi, 222, 8925, 6508, 2625
Koiwashi, 1316, 1155, 617, 328
ObaIwashi, 2716, 499, 267, 175
Mentai, 678, 1155, 859, 394
Hamachi, 4772, 998, 632, 105
Isaki, 268, 2625, 1465, 840

ふむ。前回と同様に、中値安値の関係を使うことにするか。
単回帰分析も行うことにしよう。lm()関数があるが今は使わない。

x1 <- X$Naka
x2 <- X$Yasu

# まず、推定値を求める
B <- cov(x1,x2)/var(x1)

# まず、推定値を求める
B0 <- mean(x2) - B*mean(x1)

# x2の推定値を求める
x2.hat <- B0 + B*x1

数式あった方が解り易い。x1によってx2の値を説明するので、
たしか、以下のような式になるハズであった。



ここで、二つ推定値のいうのがそれぞれ、

,  

であった。詳しい説明は既にしてあるから、ここまでは大丈夫であろう。
自信が無い人は、もう一度復習をすること。

さて、現在、この回帰式によって説明されるのは、
あくまで、理屈として推定される値であって、実際の値とは異なる
理屈上の話だから、直線で推定の直線が描けたのであった。

では、実際の値というのは、推定された値を使ってどのように表現できるか?



この式は、推定値では無くて、実際の値を表しているから、
左辺の「^(ハット)」が取れていることにも注目。

さて、この式の中で重要なことは最後に「」の値が付いていること。
これを「残差(residuals)」と呼び、この記号は一般的に残差を表す。

つまり、推定された値に誤差を加えたものが実際の値、と考えるのである。
この辺りの考え方は、数学嫌いの人には、少々、理解し難いかもしれない。

では、残差は、どうすれば求めることができるか?
これも中学校までの数学の知識があれば簡単なことであろう。



つまり、実際の値から推定値を引くだけ。混乱することは無い。
では、早速、この誤差の部分を計算してみる。

residuals <- x2 - x2.hat

ふむ。これで誤差が計算できた。ちょっと解りにくいので可視化する。
# 上手く重なるようにx軸とy軸の描画範囲を固定する
x.lim <- c(0,max(x1))
y.lim <- c(0,max(x2))

# 散布図を作成する
plot(x1, x2, xlim=x.lim, ylim=y.lim, pch=16)

# 図を重ねるためのオマジナイ
par(new=TRUE)

# xの値の範囲を最低値と最大値で決める。
x1.range <- seq(min(x1),max(x1))

# x値の範囲に対応する推定値を計算する。
x1.estim <- B0 + B*x1.range

# 回帰直線を引く
plot(x1.range, x1.estim, xlim=x.lim, ylim=y.lim, xaxt="n", yaxt="n", xlab="", ylab="", type="l", col="red")

# 残差を表す線を描画する
for(i in seq(1,length(x1))){
  x <- rep(x1[i],2)                 # x1の値を2つ作る
  y <- c(x2[i], x2[i]-residuals[i]) # x2の値とx2の値から残差を引いた値を作る

  # 残差を表す垂線を引く
  par(new=TRUE)
  plot(x, y, xlim=x.lim, ylim=y.lim, xaxt="n", yaxt="n", xlab="", ylab="", type="l", col="blue")
}

# 最後にグリッドを描いて全体を整える
grid()

この図において、実際の値から回帰直線に引かれた垂線が残差「residuals」である。
この残差というのが、二つの変数間の関係を観察する上で重要な値で、
残差を合わせた値が小さいほど当てはまりが良いことを表す



二乗しているのは、値の正負が打ち消し合わないようにするため。
これを残差平方和(Sum Squared Error)と呼ぶ。とりあえず、Se と置いておく。
試しに、今回のデータを使って、この値を確認してみる。

(se <- sum(residuals^2))

これを実行すると、以下のようになる。

> (se <- sum(residuals^2))
[1] 2076206

さて、この値が小さいということが重要なのだけれど、これは大きいのか?
この値の大小の程度を見分けるためには、やはり、基準化しないといけない。
原点に帰って、もう一度、この残差というものが何なのかを考えてみる。

データを観察する上で、最も重要なのは、くどいけれど、バラツキ
そして、個々の値のバラツキ偏差と言い、
偏差を二乗して足し合わせたものが偏差平方和

とりあえず、目的変数総平方和(Sum Squared Total Deviation)と呼び、
この値をSt とでも置いておくことにする。式は以下の通り。



これを対象数から1を引いた「n-1」で割ると?そうそう、分散になる。
要するに、分散の式の分子の部分に当たるのであった。

さて、このStSeの関係に注目してやると、
Stは観測値のバラツキの平方和で、Seは残差のバラツキの平方和だから、
予測値のバラツキとSe を足したものが、St になるはず。

では、予測値のバラツキはどうなるか?というと、以下のようになる。



つまり、St = Se + Sr という関係が成立するはず。
これは、直感的に理解できる。ここからが数学マジック!
両辺をStで割るとどういうことになるのか?



となる。ここで、残差平方和の影響の大きさを見たいので、
この式から、予測値のバラツキの部分を抜いてしまう。
すると、以下のようになる。左辺に何もないと困るので「」を置いておく。



つまり、残差平方和の大きさというのは、このように表すことができる。
この値が「1」に近づくということは、残差の影響が小さいことを意味する
なかなか、エレガントな数式。私は美しいと思うのだが、どうだろう?

さて、この「」という指標は頻繁に登場する。だからちゃんと名前がある。
一般的に、この指標のことを「決定係数」と呼び、慣例的に「」で表す。

では、この決定係数を実際に計算してみる。

# 一応、三つの値を出しておく。
st <- sum((x2-rep(mean(x2), length(x2)))^2)
sr <- sum((x2.hat-rep(mean(x2), length(x2)))^2)
se <- sum((x2-x2.hat)^2)

# 決定係数の計算
(R2 <- 1-(se/st))

では、確認。以下が実行結果。

> # 決定係数の計算
> (R2 <- 1-(se/st))
[1] 0.7576962

さて、この結果は果たして高いと言えるのかどうか?実は、判断は難しいのであるが、
一方の変数がもう一方を説明する上で、75.8%の説明力を持っていることを示していて、
これは、決して低い値ではない。大体、60〜70%以上であれば、悪くはない。

決定係数に関しては、注意するべきことがある。
相関係数と同様に、この値を盲目的に信用してはならないし、
慎重にデータの解釈をしなければならない。決定係数は、基準の一つでしかない

例えば、決定係数は、対象数が多くなると必然的に値が高くなるので、
自由度調整済み決定係数と呼ばれる指標を用いたり、
確立統計の手法を用いる場合もある。

さてさて、最後に、Rのlm()関数を使った場合についてもやっておく。
とりあえず、X.reg という変数に、回帰分析の結果を入れて、その要約を確認する。

X.reg <- lm(x2 ~ x1)
summary(X.reg)

以下がこの処理の実行結果。

> X.reg <- lm(x2 ~ x1)
> summary(X.reg)

Call:
lm(formula = x2 ~ x1)

Residuals:
    Min      1Q  Median      3Q     Max 
-678.72 -163.58   -7.29  158.81  506.60 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) -69.68447   77.21191  -0.903    0.375    
x1            0.36372    0.03958   9.189 8.45e-10 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1 

Residual standard error: 277.3 on 27 degrees of freedom
Multiple R-squared: 0.7577, Adjusted R-squared: 0.7487 
F-statistic: 84.43 on 1 and 27 DF,  p-value: 8.447e-10 

実は、色々と見ないといけないのであるが、
そもそも、今回の話の目的は、二つの変数間の関係を見ることであって、
精密な推定を行うことではない。ということで、決定係数以外は無視。

さて、上記の出力において、Coefficients の部分は係数の部分。
決定係数の部分は、下から二行目の部分。Multiple R-squared の右側。
ちゃんと、手計算で算出した決定係数と一致していることが解る。

回帰分析の解説の多くは、「目的変数説明変数によって推定する手法」、
と紹介しているのではないかと思うが、変数間の関係を見ることが重要なのである。
これは、相関係数の考え方に非常に良く似ているのである。たしかに、式も似ていた。

相関係数との違いは、一方の変数によってもう一方の変数を説明するという考え方
したがって、その説明力を「残差」に注目して検討するのである
残差が小さいほど、二つの変数の間の関係は強いということになる。

他の教科書に書かれているように、推定に注目するのであれば、
より複雑なことが必要になるし、3つ以上の変数間の関係になると結構大変。
残差そのものに関する検討や、複数の変数の当てはまりの高さの検討も必要。

この章で書いてしまいたい話ではあるが、
この辺りの話は、中級編重回帰分析の話で整理することにする。

2012/09/13

文系のための「正規分布」(2)

正規分布の存在意義というのは、結局のところ、平均標準偏差さえ分かれば、
釣り鐘状」に値が分布するような関数を作ることができ、その関数を用いることで、
ある値を取る確率を簡単に計算できるということであった。

もちろん、釣り鐘状には分布しないこともあって、
その時には、残念ながら正規分布を前提にすることはできないのであるが、
別の様々な分布を前提とすることになる。

他の様々な分布を知ることは重要ではあるが、ここでは正規分布に限った話にする。
まずは、正規分布の意味が解っていれば、他の分布関数を理解する上でも、
多少の手助けにはなるだろう。

さて。前回までの話では、一般的な正規分布の話をしてきたが、
今回の話では、少し特殊な場合での正規分布について整理する。

どのように特殊なのかというと、つまり、
平均が「0」で標準偏差が「1」となるような正規分布である。
この種の正規分布のことを、一般的には、「標準正規分布」と呼ぶ。

この標準正規分布を用いると、色々と計算が便利になるので、様々なとこに出てくる。
なぜ、標準正規分布を用いるか、という話はとりあえず置いておいて、まずは基本的な話から。

さて、前回の話では、一般的な正規分布の関数の式を以下のように表した。



ここで、μは平均を表し、σは標準偏差を表したのであった。
標準正規分布では、μ=0、σ=1 となるので、以下のように表される。



確か、正規分布は、平均標準偏差を与えれば良いだけなので、
これだけで、釣り鐘状の山を描くことができる。Rでは以下のようにする。
# Xの取り得る範囲を決める。とりあえず、-4~4くらいで。これはテキトー。
X.range <- seq(-4, 4, 0.01)

# 標準正規分布のX.range の間の f(x)の値を得る
X.dnorm <- dnorm(X.range)

# 山を描く
plot(X.range, X.dnorm, type="l", xlab="", ylab="", ylim=c(0,0.5))
title(main="Bell curve for Standard Normal Distribution")
title(xlab="Range of x value(Standard deviation)", ylab="Density")
abline(h=0)

# 平均を通る線を引く
abline(v=0, lty=2, col="red")
text(0, 0.45, expression(mu==0), cex=1.8)

この図において、x軸の値が標準偏差であり、y軸の値がその時の確率の密度を表す。
標準偏差「0」の所で山の頂点があり、ここが平均を示している。
また、±4」付近に山裾が存在しているのが確認できる。

標準正規分布において重要なことは、
平均からの距離を標準偏差の倍数で確率を推測するということであり、
区切りの良い距離として±1σ倍±2σ倍±3σ倍を用いることが多い。

とりあえず、この状況を可視化して整理してみる。まずは、±1σ倍の場合から。

なお、今回から、x軸の軸ラベルには、一般的な表記を用いるが、
平均「0」、標準偏差「1」の場合には、先ほどの図と同じ状況になる。
# 山を描く
plot(X.range, X.dnorm, type="l", xlab="", ylab="", xaxt="n", ylim=c(0,0.5))
axis(1, at=seq(-3, 3, by=1), labels=c("μ-3σ","μ-2σ", "μ-1σ", "μ", "μ+1σ", "μ+2σ", "μ+3σ"))
title(main="Bell curve for Standard Normal Distribution")
title(xlab="Range of x value(Standard deviation)", ylab="Density")
abline(h=0)

# 面積の部分を塗りつぶす
poly.x <- seq(-1,1,0.01)
poly.y <- c(0, dnorm(poly.x), 0)
polygon(c(-1, poly.x, 1), poly.y, col="lightblue")

# 平均を通る線を引く
abline(v=0, lty=2, col="red")

# キャプションを入れる。
prob <- paste(round(pnorm(1) - pnorm(-1), 3)*100, "%")
text(0, 0.1, prob, cex=1.8)

次は、±2σ倍の場合。

# 山を描く
plot(X.range, X.dnorm, type="l", xlab="", ylab="", xaxt="n", ylim=c(0,0.5))
axis(1, at=seq(-3, 3, by=1), labels=c("μ-3σ","μ-2σ", "μ-1σ", "μ", "μ+1σ", "μ+2σ", "μ+3σ"))
title(main="Bell curve for Standard Normal Distribution")
title(xlab="Range of x value(Standard deviation)", ylab="Density")
abline(h=0)

# 面積の部分を塗りつぶす
poly.x <- seq(-2,2,0.01)
poly.y <- c(0, dnorm(poly.x), 0)
polygon(c(-2, poly.x, 2), poly.y, col="lightblue")

# 平均を通る線を引く
abline(v=0, lty=2, col="red")

# キャプションを入れる。
prob <- paste(round(pnorm(2) - pnorm(-2), 3)*100, "%")
text(0, 0.1, prob, cex=1.8)

次は、±3σ倍の場合。

# 山を描く
plot(X.range, X.dnorm, type="l", xlab="", ylab="", xaxt="n", ylim=c(0,0.5))
axis(1, at=seq(-3, 3, by=1), labels=c("μ-3σ","μ-2σ", "μ-1σ", "μ", "μ+1σ", "μ+2σ", "μ+3σ"))
title(main="Bell curve for Standard Normal Distribution")
title(xlab="Range of x value(Standard deviation)", ylab="Density")
abline(h=0)

# 面積の部分を塗りつぶす
poly.x <- seq(-3, 3, 0.01)
poly.y <- c(0, dnorm(poly.x), 0)
polygon(c(-3, poly.x, 3), poly.y, col="lightblue")

# 平均を通る線を引く
abline(v=0, lty=2, col="red")

# キャプションを入れる。
prob <- paste(round(pnorm(3) - pnorm(-3), 3)*100, "%")
text(0, 0.1, prob, cex=1.8)

さて、ところで、平均「0」、標準偏差「1」、という状況から連想することはないだろうか?
確かにどこかで聞いたような。そうそう、標準化の話に似ている。
標準化の話では、標準偏差ではなく、分散が「1」と説明したが、結局は同じこと。

念のために。標準偏差を「」としたとき、分散は「」である。つまり、二乗ということ。
したがって、「」の場合、「」となる。

つまり、何が言いたいかというと、正規分布に従うデータがあって、
そのデータを標準化したデータは、標準正規分布に従うのである。
ここで、標準化の話を思い出してみる。確か、以下のような式であった。



重要なのは、右端の標準化の式である。
これを使えば、一般的な正規分布標準正規分布に変換できる。

なぜ、これが便利なのか?

前回の話の最期に、数学嫌いの人にとって目を背けたくなる積分の式が登場した。
これを実際に「手計算」で解くのは、少々厄介なのである。
もっとも、現在は、コンピュータが計算してくれるので問題は無いのであるが...。

そういった状況があって、コンピュータが存在していない時には、
標準正規分布表」というものを使っていた。

あらゆる正規分布の表を作ろうとすると、無限個の表を作らないといけないので、
要するに、標準正規分布の場合だけの表を作り、それを参照していたのである。
っということで、試しに標準正規分布表を作ってみる。

かなり、長くなってしまうので、マイナス方向は省略平均を中心に対称なので構わない。
単なる表を作るだけの作業なので、余裕の無い人はパスして構わない。

# 行数を数えるための変数を初期化する
nrow <- 0

# 結果を格納するための行列を準備する
N <- matrix(ncol=10, nrow=41)

# 標準正規分布表を作成する
for(i in seq(0, 4, by=0.1)){
    nrow <- nrow + 1    # 行数を一行進める
    ncol <- 0               # 列数を数えるための変数を初期化する
    for(j in seq(0, 0.09, by=0.01)){
        ncol <- ncol + 1  # 列数を一行進める

        # 平均から上の部分の面積を計算する。これが確立。
        N[nrow, ncol] <- round(pnorm(i+j) -pnorm(0), 4)
    }
}

# 列名と行名を入れる

rownames(N)<-seq(0, 4, by=0.1)
colnames(N)<-seq(0, 0.09, by=0.01)

そして、これを実行すると、標準正規分布表が出来上がる。

> N
         0   0.01   0.02   0.03   0.04   0.05   0.06   0.07   0.08   0.09
0   0.0000 0.0040 0.0080 0.0120 0.0160 0.0199 0.0239 0.0279 0.0319 0.0359
0.1 0.0398 0.0438 0.0478 0.0517 0.0557 0.0596 0.0636 0.0675 0.0714 0.0753
0.2 0.0793 0.0832 0.0871 0.0910 0.0948 0.0987 0.1026 0.1064 0.1103 0.1141
0.3 0.1179 0.1217 0.1255 0.1293 0.1331 0.1368 0.1406 0.1443 0.1480 0.1517
0.4 0.1554 0.1591 0.1628 0.1664 0.1700 0.1736 0.1772 0.1808 0.1844 0.1879
0.5 0.1915 0.1950 0.1985 0.2019 0.2054 0.2088 0.2123 0.2157 0.2190 0.2224
0.6 0.2257 0.2291 0.2324 0.2357 0.2389 0.2422 0.2454 0.2486 0.2517 0.2549
0.7 0.2580 0.2611 0.2642 0.2673 0.2704 0.2734 0.2764 0.2794 0.2823 0.2852
0.8 0.2881 0.2910 0.2939 0.2967 0.2995 0.3023 0.3051 0.3078 0.3106 0.3133
0.9 0.3159 0.3186 0.3212 0.3238 0.3264 0.3289 0.3315 0.3340 0.3365 0.3389
1   0.3413 0.3438 0.3461 0.3485 0.3508 0.3531 0.3554 0.3577 0.3599 0.3621
1.1 0.3643 0.3665 0.3686 0.3708 0.3729 0.3749 0.3770 0.3790 0.3810 0.3830
1.2 0.3849 0.3869 0.3888 0.3907 0.3925 0.3944 0.3962 0.3980 0.3997 0.4015
1.3 0.4032 0.4049 0.4066 0.4082 0.4099 0.4115 0.4131 0.4147 0.4162 0.4177
1.4 0.4192 0.4207 0.4222 0.4236 0.4251 0.4265 0.4279 0.4292 0.4306 0.4319
1.5 0.4332 0.4345 0.4357 0.4370 0.4382 0.4394 0.4406 0.4418 0.4429 0.4441
1.6 0.4452 0.4463 0.4474 0.4484 0.4495 0.4505 0.4515 0.4525 0.4535 0.4545
1.7 0.4554 0.4564 0.4573 0.4582 0.4591 0.4599 0.4608 0.4616 0.4625 0.4633
1.8 0.4641 0.4649 0.4656 0.4664 0.4671 0.4678 0.4686 0.4693 0.4699 0.4706
1.9 0.4713 0.4719 0.4726 0.4732 0.4738 0.4744 0.4750 0.4756 0.4761 0.4767
2   0.4772 0.4778 0.4783 0.4788 0.4793 0.4798 0.4803 0.4808 0.4812 0.4817
2.1 0.4821 0.4826 0.4830 0.4834 0.4838 0.4842 0.4846 0.4850 0.4854 0.4857
2.2 0.4861 0.4864 0.4868 0.4871 0.4875 0.4878 0.4881 0.4884 0.4887 0.4890
2.3 0.4893 0.4896 0.4898 0.4901 0.4904 0.4906 0.4909 0.4911 0.4913 0.4916
2.4 0.4918 0.4920 0.4922 0.4925 0.4927 0.4929 0.4931 0.4932 0.4934 0.4936
2.5 0.4938 0.4940 0.4941 0.4943 0.4945 0.4946 0.4948 0.4949 0.4951 0.4952
2.6 0.4953 0.4955 0.4956 0.4957 0.4959 0.4960 0.4961 0.4962 0.4963 0.4964
2.7 0.4965 0.4966 0.4967 0.4968 0.4969 0.4970 0.4971 0.4972 0.4973 0.4974
2.8 0.4974 0.4975 0.4976 0.4977 0.4977 0.4978 0.4979 0.4979 0.4980 0.4981
2.9 0.4981 0.4982 0.4982 0.4983 0.4984 0.4984 0.4985 0.4985 0.4986 0.4986
3   0.4987 0.4987 0.4987 0.4988 0.4988 0.4989 0.4989 0.4989 0.4990 0.4990
3.1 0.4990 0.4991 0.4991 0.4991 0.4992 0.4992 0.4992 0.4992 0.4993 0.4993
3.2 0.4993 0.4993 0.4994 0.4994 0.4994 0.4994 0.4994 0.4995 0.4995 0.4995
3.3 0.4995 0.4995 0.4995 0.4996 0.4996 0.4996 0.4996 0.4996 0.4996 0.4997
3.4 0.4997 0.4997 0.4997 0.4997 0.4997 0.4997 0.4997 0.4997 0.4997 0.4998
3.5 0.4998 0.4998 0.4998 0.4998 0.4998 0.4998 0.4998 0.4998 0.4998 0.4998
3.6 0.4998 0.4998 0.4999 0.4999 0.4999 0.4999 0.4999 0.4999 0.4999 0.4999
3.7 0.4999 0.4999 0.4999 0.4999 0.4999 0.4999 0.4999 0.4999 0.4999 0.4999
3.8 0.4999 0.4999 0.4999 0.4999 0.4999 0.4999 0.4999 0.4999 0.4999 0.4999
3.9 0.5000 0.5000 0.5000 0.5000 0.5000 0.5000 0.5000 0.5000 0.5000 0.5000
4   0.5000 0.5000 0.5000 0.5000 0.5000 0.5000 0.5000 0.5000 0.5000 0.5000

少々見難い出力となってしまったが、一行目は行名で、一列目は列名である。
ちょっと、特殊な見方になっていて、行方向は小数点第一位を示していて、
列方向は小数点第二位を示している。

つまり、0.36σ のときの面積であれば、
0.3」+「0.06」なので、4行目の7列目の値を見る。
すると、「0.1406」となっている。これが、平均「0」からの面積となる。

さて、ここで、分を見てみると、11行目の1列目の値なので「0.3413」となっている。
この値を2倍すると「0.6826」となり、±1σの値と一致する。
つまり、標準正規分布表は以下のような状況を示している。
# 片袖の標準正規分布の図を描く
x <- seq(0, 4, by=0.01)
y <- dnorm(x)
plot(x, y, type="l", xlab="", ylab="", xaxt="n")
title(main="Bell curve for Standard Normal Distribution")
title(xlab="Range of x value(Standard deviation)", ylab="Density")
abline(h=0)

# 面積の部分を描く
poly.x <- seq(0, 1.8, 0.01)
poly.y <- c(0, dnorm(poly.x), 0)
polygon(c(0, poly.x, 1.8), poly.y, col="lightgray")
text(0.8, 0.1, expression(integral(f(x)*dx,0,a)), cex=1.8)

現在では、すっかり、標準正規分布表を用いることは無くなったが、
かつては、この表を用いて確率を求めていたのである。

さて、重要なことは、要するに正規分布に従うあらゆるデータは、
そのデータを標準化することで標準正規分布に変換して考えることができる。

2012/09/05

文系のための「主成分分析の仕組み」(2)

前回は、特異値分解を通して主成分分析の仕組みについて整理した。
主成分分析は、「対象の本質」と「変数の本質」に分離し、
対象のみに焦点」を当てて分析する方法だった。

そして、対象の本質のみを記述した部分を主成分得点と呼び、
変数の本質のみを記述した部分を主成分負荷量と呼んだ。

また、対象のみの状態にすることで、変数間の関係から開放され、
分散のみに注目するだけで良い状況になった。

前回の話では、特異値分解を用いて主成分分析を行ったが、
もちろん、Rには主成分分析の関数が存在している。しかも、色々。

そこで、今回は最初にRにおける主成分分析関数を使ってみることにする。

今回もGoogle Spreadsheet にデータを置いてあるので、それを利用する。

library(RCurl)

データの詳細は、散布図行列の話にあるので、そちらを参照する。
ライブラリを無事に読み込めたら、次は、以下のコマンドを実行する。

# Google Spreadsheet からデータをダウンロードする
data <- getURL("https://docs.google.com/spreadsheet/pub?key=0AtOtIs5BjRVhdHo0c0pjb29OTU9aV3BmQUFJUWJQa0E&single=true&gid=0&range=A1%3AK48&output=csv")

# ダウンロードしたデータを読み込む
X <- as.matrix(read.table(textConnection(data), header=TRUE, row.names=1, sep=","))

まずは、分散共分散行列相関係数行列散布図行列を確認する。
毎回、これらを確認することを忘れてはいけない。
# 散布図行列を作成するためにpsych ライブラリを読み込む。
library(psych)

# 分散共分散行列を出す
round(cov(X), 3)

# 相関係数行列を出す
round(cor(X), 3)

#  散布図行列を出す。
pairs.panels(X, scale=TRUE, smooth=FALSE, ellipses=FALSE, density=FALSE)

分散共分散行列と相関係数行列の出力結果は以下の通り。

> # 散布図行列を作成するためにpsych ライブラリを読み込む。
> library(psych)
>
> # 分散共分散行列を出す
> round(cov(X), 3)
         HM    ELD      HC    CHR     SC     LI     SP    ENV    DIS      IC
HM   27.370  0.750   5.470 -2.063  7.600  2.305 -5.029  5.586 -1.925 -12.463
ELD   0.750 38.669  22.176 10.859 -2.048  9.253  4.594 16.180 -0.199  44.576
HC    5.470 22.176 103.638 11.745  6.029  9.985  3.673 12.545 -0.205  64.775
CHR  -2.063 10.859  11.745 13.820 -0.021  7.465  8.619 12.185  0.559  11.328
SC    7.600 -2.048   6.029 -0.021 52.300 -2.108  0.040  1.468  1.434   7.519
LI    2.305  9.253   9.985  7.465 -2.108 12.677  5.382 20.473  0.950   0.394
SP   -5.029  4.594   3.673  8.619  0.040  5.382 21.932 12.175  1.550  13.332
ENV   5.586 16.180  12.545 12.185  1.468 20.473 12.175 58.090  0.927  -2.488
DIS  -1.925 -0.199  -0.205  0.559  1.434  0.950  1.550  0.927  7.540  12.452
IC  -12.463 44.576  64.775 11.328  7.519  0.394 13.332 -2.488 12.452 279.265
>
> # 相関係数行列を出す
> round(cor(X), 3)
        HM    ELD     HC    CHR     SC     LI     SP    ENV    DIS     IC
HM   1.000  0.023  0.103 -0.106  0.201  0.124 -0.205  0.140 -0.134 -0.143
ELD  0.023  1.000  0.350  0.470 -0.046  0.418  0.158  0.341 -0.012  0.429
HC   0.103  0.350  1.000  0.310  0.082  0.275  0.077  0.162 -0.007  0.381
CHR -0.106  0.470  0.310  1.000 -0.001  0.564  0.495  0.430  0.055  0.182
SC   0.201 -0.046  0.082 -0.001  1.000 -0.082  0.001  0.027  0.072  0.062
LI   0.124  0.418  0.275  0.564 -0.082  1.000  0.323  0.754  0.097  0.007
SP  -0.205  0.158  0.077  0.495  0.001  0.323  1.000  0.341  0.121  0.170
ENV  0.140  0.341  0.162  0.430  0.027  0.754  0.341  1.000  0.044 -0.020
DIS -0.134 -0.012 -0.007  0.055  0.072  0.097  0.121  0.044  1.000  0.271
IC  -0.143  0.429  0.381  0.182  0.062  0.007  0.170 -0.020  0.271  1.000

前回は、svd()関数を使って主成分分析を行った。まずは、その復習。
偏差行列を出す処理は簡略化。まとめて実行。

# 行列の引き算ができるように、平均値を総数個複製する。
M <- matrix(rep(m <- t(colMeans(X)), each=nrow(X)), nrow=nrow(X))

# 元の行列から、偏差行列を作成する。
A <- as.matrix(X-M)

# 特異値分解を行う
A.svd <- svd(A)

# 主成分スコアと主成分負荷量を計算する
A.svd.score <- A.svd$u %*% diag(A.svd$d)
A.svd.loadings <- A.svd$v %*% diag(A.svd$d)

これで主成分分析は完了。主成分得点主成分負荷量が出せた。
今回は、この結果とRの主成分分析の関数との比較するところから始める。

実は、Rの主成分分析の関数は一つではなく複数存在し、少しずつ異なる
R の関数は、世界中の大多数の研究者、技術者、学生らによって開発されているので、
様々な手法が存在し得る。そのあたりが、商用製品と大きく異なるところ。

雑多という批判を受けることもあるが、中身がはっきりと見えるし、
問題があれば自分で修正もできる。自分で開発した関数を公開することもできる。
それゆえに、従来の商用製品よりも信頼性は高いと言われている。

さて、話は少し逸れたが、有名な主成分分析の関数は以下の通り。
  • princomp()関数:stats パッケージの主成分分析の関数。そのまま使える。
  • prcomp()関数:同じく、stats パッケージの主成分分析の関数。そのまま使える。
  • PCA()関数FactoMineR パッケージの主成分分析の関数。インストールが必要
では、これらの内、どれを使うべきか?難しい問題である。

princomp()関数は、色々と問題があって使うべきではない
prcomp()関数は、理論通りの方法。ただし、バイプロットと呼ばれる図では注意が必要。
PCA()関数は、非常に多機能。ただし、色々と値が調整されているので注意が必要。

とりあえずは、最も無難そうなprcomp()関数を使うことにする。

# prcomp()関数で主成分分析を行い、変数に格納する。
X.pca <- prcomp(X)

# 分析結果の格納状態を確認する。
str(X.pca)

結果がどのように格納されているかというと以下の通り。

> str(X.pca)
List of 5
 $ sdev    : num [1:10] 17.68 10.04 8.1 7.32 5.32 ...
 $ rotation: num [1:10, 1:10] 0.0341 -0.1811 -0.3131 -0.0574 -0.0318 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : chr [1:10] "HM" "ELD" "HC" "CHR" ...
  .. ..$ : chr [1:10] "PC1" "PC2" "PC3" "PC4" ...
 $ center  : Named num [1:10] 13.8 32.3 26.6 19.8 41.9 ...
  ..- attr(*, "names")= chr [1:10] "HM" "ELD" "HC" "CHR" ...
 $ scale   : logi FALSE
 $ x       : num [1:47, 1:10] 3.65 19.79 4.19 -17.59 -4.17 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : chr [1:47] "Hokkaido" "Aomori-ken " "Iwate-ken " "Miyagi-ken" ...
  .. ..$ : chr [1:10] "PC1" "PC2" "PC3" "PC4" ...
 - attr(*, "class")= chr "prcomp"

では、特異値分解で行った方法と順番に比較する。
今回は、長くなるので出力だけ。出力が同じであることが確認できれば良い。
  • $ sdev は、主成分得点標準偏差
> # prcomp()関数の$sdev の中身
> X.pca$sdev
 [1] 17.678171 10.038151  8.099573  7.317139  5.324851  4.775684  3.889277
 [8]  2.695817  2.462664  1.804632

> # 特異値分解の場合。エラーが出るがここでは無視。
> sd(A.svd.score)
 [1] 17.678171 10.038151  8.099573  7.317139  5.324851  4.775684  3.889277
 [8]  2.695817  2.462664  1.804632
  • $ rotation は、特異値ベクトル「V」のこと。つまり、「変数の本質の回転
> # prcomp()関数の$rotation の中身、長いので一部のみ。
> head(round(X.pca$rotation, 3))
       PC1    PC2    PC3    PC4    PC5    PC6    PC7    PC8    PC9   PC10
HM   0.034 -0.146  0.116 -0.220 -0.724  0.294 -0.544  0.063 -0.009 -0.063
ELD -0.181 -0.233 -0.245  0.055 -0.352 -0.807  0.005 -0.114 -0.247 -0.029
HC  -0.313 -0.673  0.598  0.244  0.141  0.065  0.037 -0.038 -0.066 -0.028
CHR -0.057 -0.181 -0.156  0.006  0.188 -0.220 -0.267  0.523  0.616 -0.364
SC  -0.032 -0.054  0.275 -0.915  0.176 -0.204  0.089 -0.006  0.012  0.059
LI  -0.021 -0.244 -0.213 -0.006 -0.012  0.038  0.000  0.384  0.072  0.860

> # 特異値分解の場合。名前は入ってないが同じ。
> head(round(A.svd$v,3))
       [,1]   [,2]   [,3]   [,4]   [,5]   [,6]   [,7]   [,8]   [,9]  [,10]
[1,]  0.034 -0.146  0.116 -0.220 -0.724  0.294 -0.544  0.063 -0.009 -0.063
[2,] -0.181 -0.233 -0.245  0.055 -0.352 -0.807  0.005 -0.114 -0.247 -0.029
[3,] -0.313 -0.673  0.598  0.244  0.141  0.065  0.037 -0.038 -0.066 -0.028
[4,] -0.057 -0.181 -0.156  0.006  0.188 -0.220 -0.267  0.523  0.616 -0.364
[5,] -0.032 -0.054  0.275 -0.915  0.176 -0.204  0.089 -0.006  0.012  0.059
[6,] -0.021 -0.244 -0.213 -0.006 -0.012  0.038  0.000  0.384  0.072  0.860
  • $ center というのは、元のデータの真ん中。列平均値特異値分解とは無関係
> # prcomp()関数の$center の中身。長いので一部のみ。
> round(X.pca$center ,3)
    HM    ELD     HC    CHR     SC     LI     SP    ENV    DIS     IC
13.804 32.349 26.585 19.849 41.885 12.053 16.532 24.983  7.028 25.081

> # 特異値分解とは無関係。元データの列平均。
> round(colMeans(X), 3)
    HM    ELD     HC    CHR     SC     LI     SP    ENV    DIS     IC
13.804 32.349 26.585 19.849 41.885 12.053 16.532 24.983  7.028 25.081
  • $ scale なんだ、コレ!? 実は、これが今日の話のメインに関係説明は後回し
> # prcomp()関数の$scale の中身。とりあえず、中身の確認だけ。
> X.pca$scale
[1] FALSE
  • $ x これは、主成分得点
> # prcomp()関数の$x の中身。長いので一部のみ。
> head(round(X.pca$x ,3)[,1:5])
                 PC1    PC2    PC3     PC4    PC5
Hokkaido       3.651 -7.957  5.140   1.052  2.310
Aomori-ken    19.786  5.058 12.888 -11.767  0.633
Iwate-ken      4.191 18.646 -4.698   7.407  3.635
Miyagi-ken   -17.588 -1.574  2.599  -2.082 -9.318
Akita-ken     -4.172 16.770 10.396   3.749  6.742
Yamagata-ken  21.314 14.733  1.865   9.510  1.088

> # 特異値分解の場合。名前は入ってないが同じ。
> head(round(A.svd.score,3)[,1:5])
        [,1]   [,2]   [,3]    [,4]   [,5]
[1,]   3.651 -7.957  5.140   1.052  2.310
[2,]  19.786  5.058 12.888 -11.767  0.633
[3,]   4.191 18.646 -4.698   7.407  3.635
[4,] -17.588 -1.574  2.599  -2.082 -9.318
[5,]  -4.172 16.770 10.396   3.749  6.742
[6,]  21.314 14.733  1.865   9.510  1.088

以上のように、prcomp()関数は、特異値分解によって主成分分析をしていることが解る。
なお、主成分負荷量に関しては計算されておらず、
主成分プロット負荷量をプロットする際に内部的に計算される

さて、後回しにしていたが、唯一、なかったものが $ scale の部分だった。
これは一体、何だろうか?scale という言葉に見覚えは無いだろうか?
そうそう。確か、標準化のことを scaling と呼んだ。平均「0」で分散「1」

実は、主成分分析は、分散共分散行列に対応させる方法と、
相関係数行列に対応させる方法の二種類の方法が存在する。

なぜか?よく考えてみると難しい話ではない。

要するに、各変数の単位同じ場合異なる場合があって、
単位が異なる場合には、全体的に大きな値の入った変数に影響されるから

例えば、身長、体重、血圧、体温という変数があるデータがあったとする。
このとき、身長をミリ体重をキロ血圧をパスカル体温を摂氏
でそれぞれ計測していたとする。つまり、単位がバラバラの状況

この状況というのは、実は、別の単位で置き換えることもできる。
身長をメートル体重をグラム血圧をミリ体温を華氏、といった具合に。

この二つのデータは、単位が変わっただけで、本質的には何も変わらないが、
分散共分散行列の結果は、値に応じて変わってしまう
一方、相関係数行列の場合は、平均と分散で調整しているため結果は同じ。

つまり、相関係数行列に対応するように主成分分析を行うことで、
単位の問題を解決することができる

また、単位そのものが一緒であっても、変数間の値の差が明らかに大きい場合で
かつ、そういった状況が望ましくないような場合には、
相関係数行列に対応した方法の方が適することがある

さて、では、実際に相関係数行列に対応した方法を検討してみる。

# 偏差行列の代わりに、標準化したデータを用いる
Z <- scale(X)

# 特異値分解を行う
Z.svd <- svd(Z)

# 主成分得点と主成分負荷量を変数に格納する
Z.svd.score <- Z.svd$u %*% diag(Z.svd$d)
Z.svd.loadings <- Z.svd$v %*% diag(Z.svd$d)

# 主成分得点の一部を表示する
head(round(Z.svd.loadings, 3)[,1:5])

この結果を、prcomp()関数と比較する。

# prcomp()関数のscaleパラメータをTRUEに変更する。
prcomp(X, scale=TRUE)

# 主成分得点の一部を表示する。
head(round(X.pca$x ,3)[,1:5])

標準化された行列で特異値分解をした結果とprcomp()関数の出力結果を比較する。

> head(round(Z.svd.score,3)[,1:5])
       [,1]   [,2]   [,3]   [,4]   [,5]
[1,] -0.595  0.260  0.268  0.518 -0.520
[2,]  3.213  0.435  1.089 -1.717  0.079
[3,]  1.798 -1.489 -2.021 -0.433  1.478
[4,] -1.161 -1.124  1.607 -1.371  2.359
[5,]  2.841 -2.078 -0.574 -0.500  0.656
[6,]  2.837 -0.149 -1.400  1.052  0.369

> # 主成分得点の一部を表示する。
> head(round(X.pca$x ,3)[,1:5])
                PC1    PC2    PC3    PC4    PC5
Hokkaido     -0.595  0.260  0.268  0.518 -0.520
Aomori-ken    3.213  0.435  1.089 -1.717  0.079
Iwate-ken     1.798 -1.489 -2.021 -0.433  1.478
Miyagi-ken   -1.161 -1.124  1.607 -1.371  2.359
Akita-ken     2.841 -2.078 -0.574 -0.500  0.656
Yamagata-ken  2.837 -0.149 -1.400  1.052  0.369

このように、データを標準化した結果と同じなり、
変数間の単位の差は、調整されている

ところで、主成分分析の結果として出力される分散は、
元の行列の分散共分散特異値に対応するのだった。

また、データを標準化したデータの分散共分散行列は、
相関係数行列に一致するのだった。

# 分散共分散に対応する主成分の分散
round(svd(cov(X))$d, 3)

# 相関係数行列に対応する主成分の分散
round(svd(cor(X))$d, 3)

# prcomp()関数の場合。確か、標準偏差の二乗が分散だった。
round((X.pca$sdev)^2, 3)

以下が、上記の実行結果。


> # 分散共分散に対応する主成分の分散
> round(svd(cov(X))$d, 3)
 [1] 312.518 100.764  65.603  53.541  28.354  22.807  15.126   7.267   6.065   3.257

> # 相関係数行列に対応する主成分の分散
> round(svd(cor(X))$d, 3)
 [1] 3.012 1.507 1.349 1.100 0.872 0.629 0.569 0.468 0.295 0.198

> # prcomp()関数の場合。確か、標準偏差の二乗が分散だった。
> round((X.pca$sdev)^2, 3)
 [1] 3.012 1.507 1.349 1.100 0.872 0.629 0.569 0.468 0.295 0.198


さて、相関係数行列に対応する主成分分析を行った場合、
出てきた分散は、非常に面白い性質を持っている。

# とりあえず、平均を取ってみる。
mean(svd(cor(X))$d)

そして、その実行結果。


> mean(svd(cor(X))$d)
[1] 1


平均は「1」標準化は、平均が「0」で分散が「1」となるような調整であったので、
当然、分散の平均は「1」となるのであるが、個々の値を見ると「1」以上のものもある

このことから、主成分分析を行うと、元の分散を個々の再配分していることが解る。

さて、今回は、サンプルデータを用いて、
分散共分散行列相関係数行列の二つの状況に対応する主成分分析を行ったが、
用いたサンプルデータの場合は、どちらが適切であろうか?

データの性質を考えてみると、全ての変数の値は、
ボランティア活動に費やした「1年当たりの活動従事日数」であった。

つまり、変数間の単位は一定であり、変数間に差はあるものの、
その差の大きさも重要な情報である

したがって、分散共分散行列の方が適しているように思える。

さて、ここまで二回に分けて、主成分分析の仕組みを解説してきたが、
仕組みを理解し、実行したただけでは、まだ、利用できたと言えない。

重要なことは、結果を「読み取る」こと

次回からは、可視化の方法も含めて、結果を読み取る方法について考えることにする。

2012/09/01

文系のための「多次元データの要約」(1)

今回は、分散と共分散を楽して「行列」を使って計算するという話。

まずは、復習から。分散の計算は、以下の式で表すことができた。
本来は、二乗の形で表すが、共分散の式との比較のため、このように表す。



一方、共分散の計算は、以下のように表した。
共分散の場合は、二つの変数間のバラツキを表すので、
 と  の二つの変数が使われている。



さて、ここからが今回の話の重要なポイント。
今回は、ちょっとした、行列の掛け算の魔法を垣間見ることになる。

まずは、 」分散の計算から考えてみる。
以下は、 」偏差であり、 」という記号で表した。
「〜」の記号は、チルダと呼ぶのだった。



ここで、 というベクトルの計算を考えてみる。これを展開するとどのようになるか?
tの記号は、転置(transpose)を意味したのだから、


このようになる。これは、どのような計算だったか?
忘れた人は、文系のための内積(1)を参照。

確か、「横✕縦+横✕縦+横✕縦・・・となるから・・・。」

おぉ、なるほど。二乗したのを足していく訳か。
あるベクトルの転置と元のベクトルの掛け算は「二乗和」になるのだった。

したがって、以下のようになる。



うん?これは、確か分散の式のΣの内側と同じ計算となっている。
何が言いたいかと言うと、分散の計算をベクトルで表すと、以下のようになる。



 の分散は、見事にベクトルの式で表すことができる。
確か、共分散のΣの中身は、添字が異なるだけで、分散と同じだった。
つまり、掛ける側のベクトルを とすると、



となり、したがって、



という計算になる。素晴らしい。分散と同様に見事に計算できる
分散と同様に、ベクトルの式で表すと、次のようになる。



ここまでが理解できれば大丈夫。ここまでは、ベクトルの掛け算であったが、
次のような行列を考えてみるとどうなるか?



ちょっと、難しいかもしれないが、落ち着いて考えれば大丈夫。
頑張れる人は、手計算で。自信が無い人は、行列の計算の話を参考に。
頑張れない人は、あとでRを使って練習。

計算の順番と解となる行列での位置は次のようになる。
  • 1行目の横ベクトル×1列目の縦ベクトル(  ) ⇒  Answer[1, 1]
  • 1行目の横ベクトル×2列目の縦ベクトル(  ) ⇒  Answer[1, 2]
  • 1行目の横ベクトル×3列目の縦ベクトル(  ) ⇒  Answer[1, 3]
  • 2行目の横ベクトル×1列目の縦ベクトル(  ) ⇒  Answer[2, 1]
  • 2行目の横ベクトル×2列目の縦ベクトル(  ) ⇒  Answer[2, 2]
  • 2行目の横ベクトル×3列目の縦ベクトル(  ) ⇒  Answer[2, 3]
  • 3行目の横ベクトル×1列目の縦ベクトル(  ) ⇒  Answer[3, 1]
  • 3行目の横ベクトル×2列目の縦ベクトル(  ) ⇒  Answer[3, 2]
  • 3行目の横ベクトル×3列目の縦ベクトル(  ) ⇒  Answer[3, 3]
つまり、以下のように書き換えることが可能。



ここで、注意して見てもらいたいのが、対角成分に何が入っているか?ということ。
自分同士の共分散ということは、何を意味するのだったか?答えは、分散

もうひとつのポイントは、二つの変数の共分散の計算は、
順番を変えても答えは同じ(スカラーでは、A×B=B×A)なので、
対角成分を挟んで上側と下側が同じになる、ということも重要。

したがって、この行列の計算を通して計算された結果は、
対角成分に分散が並び、それ以外に共分散が入った対象行列である。

これを、「分散共分散行列」と呼ぶ。

さらに、元の行列が標準化されていた場合、すなわち、
平均が「0」で分散が「1」となるように基準化されていた場合には、
対角成分が全て「1」で、それ以外の成分が相関係数となる

例えば、先ほどの行列 を標準化した行列を Z としたとき、



という関係が成り立つ。これを、「相関係数行列」と呼ぶ。

分散共分散行列相関係数行列は、データを要約した指標として重要であると同時に、
この行列を用いることで、主成分分析の計算が容易になる。この話は、いずれ。

では、今度は実際のデータを用いて計算の練習をしてみる。
サンプルデータは、分散標準偏差の説明で用いたものを再び使う。

# Windows と Linux の人は次のコマンド
X <- read.table("clipboard", sep=",", header=TRUE)

# Mac の人は次のコマンド
X <- read.table(pipe("pbpaste") , sep=",", header=TRUE)

以下が、そのデータ。前回のものを再掲しただけ。
データの説明に関しては、平均の話を参照。

Oroshi_Kg, Taka, Naka, Yasu
Tai, 1336, 3150, 1217, 53
Chinu, 226, 630, 513, 105
Sawara, 212, 1575, 1259, 840
Yazu, 4834, 840, 315, 179
Suzuki, 618, 1575, 1146, 525
Akou, 21, 4725, 3129, 1575
Kochi, 28, 2100, 874, 210
Okoze, 28, 5250, 2635, 210
Ainame, 29, 3150, 621, 315
Hage, 1205, 1890, 383, 210
Konoshiro, 21, 2100, 1100, 105
Sayori, 12, 5250, 3916, 1260
Anago, 1637, 2625, 1721, 630
Mebaru, 330, 4200, 1680, 315
Tachiuo, 1211, 2310, 930, 105
Hamo, 1622, 3938, 592, 53
Karei, 41, 6300, 3178, 525
Hirame, 540, 2100, 1496, 210
Aji, 5149, 3150, 789, 210
Saba, 5413, 3675, 625, 53
Ika, 2414, 2888, 1083, 105
Tako, 1844, 1890, 1180, 525
Ebi, 560, 7350, 1420, 525
KurumaEbi, 222, 8925, 6508, 2625
Koiwashi, 1316, 1155, 617, 328
ObaIwashi, 2716, 499, 267, 175
Mentai, 678, 1155, 859, 394
Hamachi, 4772, 998, 632, 105
Isaki, 268, 2625, 1465, 840

まずは、行列 と同じサイズで、平均が並んでいる行列が必要だから、

# ステップ1:データの総数(n)を求める。
n <- nrow(X)
# ステップ2:卸売数量の平均を求める。転置が必要。
m <- t(colMeans(X))
# ステップ3:行列の引き算ができるように、平均値を総数個複製する。
M <- matrix(rep(m, each=n), nrow=n, ncol=4)
# ステップ4:元の行列から、平均値が複製された行列Mを引き算する。
tilde_X <- as.matrix(X-M)

最終的に、「tilde_X」という変数に偏差行列が入った。
ここで、分散共分散の式をそのまま当てはめると、

(t(tilde_X) %*% tilde_X)/(n-1)

となり、実行結果は以下のようになる。

> (t(tilde_X) %*% tilde_X)/(n-1)
           Oroshi_Kg       Taka      Naka      Yasu
Oroshi_Kg  2814792.6 -1026368.1 -920404.3 -335784.2
Taka      -1026368.1  4210888.7 2159783.0  685439.7
Naka       -920404.3  2159783.0 1752709.8  637497.8
Yasu       -335784.2   685439.7  637497.8  306021.7

疑い深い人は、分散共分散の話の結果と照合してみよう。
間違いなく、対角成分には分散が並び、それ以外には共分散が入っている。

さて、今度は、元のデータを標準化して計算をしてみる。

# 標準化した行列を作る
Z <- scale(X)

# 共分散の計算。平均は0なので偏差行列は不要
(t(Z) %*% Z)/(n-1)

実行結果は以下の通り。

> cov(Z)
           Oroshi_Kg       Taka       Naka       Yasu
Oroshi_Kg  1.0000000 -0.2981213 -0.4143816 -0.3617937
Taka      -0.2981213  1.0000000  0.7950020  0.6038183
Naka      -0.4143816  0.7950020  1.0000000  0.8704575
Yasu      -0.3617937  0.6038183  0.8704575  1.0000000

対角成分には「1」が並んでいて、それ以外の所は相関係数が入っている。
このようにして、相関係数行列を計算することができる。

ちなみに、前回、登場したcov()関数cor()関数は、
両方とも行列に対応しているので、共分散行列相関係数行列を出せる。

cov(X)    # 分散共分散行列を直接計算する
cor(X)    # 相関係数行列を直接計算する

以下は、実行結果。

> cov(X)    # 分散共分散行列を直接計算する
           Oroshi_Kg       Taka      Naka      Yasu
Oroshi_Kg  2814792.6 -1026368.1 -920404.3 -335784.2
Taka      -1026368.1  4210888.7 2159783.0  685439.7
Naka       -920404.3  2159783.0 1752709.8  637497.8
Yasu       -335784.2   685439.7  637497.8  306021.7

> cor(X)    # 相関係数行列を直接計算する
           Oroshi_Kg       Taka       Naka       Yasu
Oroshi_Kg  1.0000000 -0.2981213 -0.4143816 -0.3617937
Taka      -0.2981213  1.0000000  0.7950020  0.6038183
Naka      -0.4143816  0.7950020  1.0000000  0.8704575
Yasu      -0.3617937  0.6038183  0.8704575  1.0000000

手計算の場合には、結局、展開して計算しないといけないので、
手間は同じであるが、Rのように行列の計算ができるソフトウェアを使えば、
分散共分散相関係数を一度に計算することができ、便利なのである。