jnobuyukiのブログ

JavaScriptとR言語を中心に研究活動に役立つwebアプリケーション技術について考えていきます。twitter ID: @j_nobuyuki

なぜ分散分析には自由度が2つあるのか?

今回は、分散分析という解析方法の紹介をします。

平均値の差の検定です。

分散分析は、3つ以上の条件やグループがあるときの平均値の条件差・グループ間の差を比較するときに使います。確率の考え方を取り入れることで、平均の差が偶然と呼べる程度の差なのか、それとも偶然からはありえないほど大きな差になっているのかを明らかにできます。分散分析という名前(英語だとANalysis Of VArianceで大文字の部分をとってANOVAとも言われます )から想像すると、分散の大きさの比較なのかなと思うかもしれませんが、実は平均の比較をしています。

平均の差の大きさをどうやって評価するのか。

条件やグループの平均の差は、同一条件、同一グループのばらつきに対する相対的な大きさとして評価します。統計的仮説検定としての分散分析は、条件、グループの平均値の差が0であることを帰無仮説*1にします。つまり、観測された条件間のばらつきは偶然得られたに過ぎず、その大きさは、条件内・グループ内の変動と等しいとなります。これを比で示したのがF値という統計量です。

 \frac{条件間・グループ間の平均値の差}{条件内・グループ内のデータのばらつき}

そしてこのF値という統計量には2つの自由度が伴います。

なぜ2つの自由度があるのか

2つの自由度はそれぞれ「分子の自由度」と「分母の自由度」と呼ばれることがあります。実は、これが上記の式に対応しており、分子の自由度は条件・グループの平均の自由度、分母の自由度はグループ内のデータのばらつきの自由度を示しています。以下では3つの条件を比較する1要因分散分析の場合で考えてみます。

条件・グループの平均の自由度

では、分子の自由度の意味を少し考えてみます。分子の自由度は
 条件数・グループ数 - 1
です。例えば3つのグループを比較するときの自由度は2になります。自由度というのは、値の取り方の自由度です。グループを考慮に入れない全体の平均値がわかっているとき、3つのうち2つのグループの平均値がわかると残り一つのグループの平均値は自然に決まります。
図式的に考えれば以下のようになります。

f:id:jnobuyuki:20160210101912j:plain

各グループの全てがグループの平均値と一致している状態です。データがたくさんあっても、データの値の取り方は限られており、全体平均がわかっているなら、あと2つの値は自由にとることができます。

条件内・グループ内のばらつきの自由度

次は、条件内・グループ内のデータのばらつきです。ここでも自由度の値の取り方は決まっています。グループの平均値がもとまっていれば、グループ内に含まれるデータの最後の1つは、他の値がわかると自然に決まります。
 グループ内のデータ数 -1
これを全てのグループについて足し合わせるので、以下のようになります。
 データ数 - 条件数・グループ数

図式的に考えれば、以下のように、各データのグループ平均からのズレを考えていることになります。

f:id:jnobuyuki:20160210102514j:plain

Rを使ってF値を求める

ではR言語F値を計算してみます。基本的には以下のlm関数またはaov関数を利用すれば簡単に計算可能です。

lm関数/ aov関数の利用

特に1つの被験者間要因分散分析ならlm関数かaov関数かで計算結果が変わらないので、どちらでも好きな方を使ってください。

#サンプルデータ
dat <- data.frame(dv = c(3.188957,  3.188957,  3.188957,  3.188957,  3.188957,  6.375809,  6.375809,  6.375809,  6.375809,  6.375809, -9.016713, -9.016713, -9.016713, -9.016713, -9.016713), iv = c("red", "red", "red",  "red", "red", "orange", "orange", "orange", "orange", "orange", "blue", "blue", "blue", "blue", "blue"))
#lm関数の場合

res_lm <- lm(dv ~ iv, data =dat)
anova(res_lm)

#aov関数の場合
res_aov <- aov(dv ~ iv, data  =dat)
summary(res_aov)

条件間・グループ間の平均値の差と条件内・グループ内のデータのばらつきを比較する

次に、条件間・グループ間の平均値の差をグループ内のデータのばらつきを比較するすることを具体的な数値計算とともに考えてみましょう。

仮想のデータセットをつくる

ここでは、本来全く差がない3つのグループを考えます。R言語のrnorm関数では、任意の平均値と分散のデータをランダムに作成できるので、これを利用しましょう。

dat <- data.frame(dv = c(rnorm(5),rnorm(5),rnorm(5)),iv = c("red", "red", "red",  "red", "red", "orange", "orange", "orange", "orange", "orange", "blue", "blue", "blue", "blue", "blue")) 

このデータにはred,orange,blueの3つのグループがあり、それぞれ平均0、分散1の分布から5つのデータがサンプルとして取られています*2

条件間・グループ間の平均値のばらつきの分布

条件間・グループ間の平均値が、全体の平均に比べてどの程度ばらつくのかは、平方和という値で考えます。各データが、グループの平均値に一致しているとみなして、それが全体の平均からのズレを計算し、二乗します。それを足し合わせたものが平方和です。二乗は平方とも呼ばれるので、二乗したものの合計という意味で平方和という言い方をしています。
R言語を利用するなら例えば以下のように計算できます。

#データ全体の平均値を計算する
mean_dat <- mean(dat$dv)

#各グループの平均値を計算する
mean_red <- mean(dat[dat[,"iv"]== "red","dv"])
mean_orange <- mean(dat[dat[,"iv"]== "orange","dv"])
mean_blue <- mean(dat[dat[,"iv"]== "blue","dv"])


#平方和を求める
MSS_group <- ((mean_red - mean_dat)^2*length(dat[dat[,"iv"]== "red","dv"]) + (mean_orange - mean_dat)^2*length(dat[dat[,"iv"]== "orange","dv"]) + (mean_blue - mean_dat)^2*length(dat[dat[,"iv"]== "blue","dv"]))

これを何度も繰り返すと、以下のように平方和の分布を求めることができます。

#上述の平方和を求める数式を関数にする
getSS_between <- function(){

dat <- data.frame(dv = c(rnorm(5),rnorm(5),rnorm(5)),iv = c("red", "red", "red",  "red", "red", "orange", "orange", "orange", "orange", "orange", "blue", "blue", "blue", "blue", "blue")) 

#データ全体の平均値を計算する
mean_dat <- mean(dat$dv)

#各グループの平均値を計算する
mean_red <- mean(dat[dat[,"iv"]== "red","dv"])
mean_orange <- mean(dat[dat[,"iv"]== "orange","dv"])
mean_blue <- mean(dat[dat[,"iv"]== "blue","dv"])


#平方和を求める
SS_between <- ((mean_red - mean_dat)^2*length(dat[dat[,"iv"]== "red","dv"]) + (mean_orange - mean_dat)^2*length(dat[dat[,"iv"]== "orange","dv"]) + (mean_blue - mean_dat)^2*length(dat[dat[,"iv"]== "blue","dv"]))

return(MSS_group)
}

#100000回繰り返す
res <- replicate(100000,getSS_between())

実際にやってみたところ、このような分布ができました。
f:id:jnobuyuki:20160210235924j:plain

理論的には、自由度2のカイ二乗分布となります。

条件内・グループ内のデータのばらつき

では、次に、条件内・グループ内のデータのばらつきを考えてみましょう。先ほどのグループ間の計算と同じように、平均値からのズレを二乗したものを合計して平方和を求めます。

#グループ内の平方和を求める関数
getSS_within <- function(){

dat <- data.frame(dv = c(rnorm(5),rnorm(5),rnorm(5)),iv = c("red", "red", "red",  "red", "red", "orange", "orange", "orange", "orange", "orange", "blue", "blue", "blue", "blue", "blue")) 

#各グループの平均値を計算する
mean_red <- mean(dat[dat[,"iv"]== "red","dv"])
mean_orange <- mean(dat[dat[,"iv"]== "orange","dv"])
mean_blue <- mean(dat[dat[,"iv"]== "blue","dv"])


#平方和を求める
SS_within <- sum((dat[dat[,"iv"] == "red","dv"] - mean_red)^2) + sum((dat[dat[,"iv"] == "orange","dv"] - mean_orange)^2) + sum((dat[dat[,"iv"] == "blue","dv"] - mean_blue)^2)

return(SS_within)
}

#100000回繰り返す
res <- replicate(100000,getSS_within())

実際に100000回繰り返して分布を描き出すと以下のようになります。
f:id:jnobuyuki:20160211000838j:plain
これは理論として自由度12(データ数15から3を引いた)のカイ二乗分布に一致しました。

F分布

F分布は、2つの平方和のそれぞれを自由度で割った値(平均平方和)の比で求めます。分布の形状としては、分子であるグループ間の平均値のばらつきを示す平方和の分布形状に影響を受けやすく、類似します。

まとめ

以上、長くなりましたが、分散分析の2つの自由度は、条件間・グループ間の平均値の差に関する自由度と条件内・グループ内のデータのばらつきについての自由度をあらわてしていることをみてきました。途中で示したように、lm関数やaov関数を利用すればあっという間にF値とp値が求まるのですが、たまにはこうやって計算を分解して見ながら考えると、統計量をより深く理解できますね。

*1:検定を行う人の考えとは逆のアイデアです。これを前提とすると、観測データの実現確率が非常に低いことを示して、帰無仮説のアイデアを否定します。逆説的なロジックなので少しわかりにくいです。数学の背理法のイメージに近いです。

*2:本当はrnorm(15)としてもいいのですが、3つのグループからとったことを意識できるようにあえて3つに分けました