Excel VBAでフィッシャーの正確検定ができる関数を作ってみた1
『Excelで学ぶコレスポンデンス分析』の付録に、フィッシャーの正確検定の解説があります。
曰く、
「2行2列でなおかつ期待度数が5未満のマス目が存在するクロス表」において「2変数が関連しているかどうか」を検定したい場合は、独立性の検定よりもフィッシャーの正確検定のほうが適しているといわれています。
『Excelで学ぶコレスポンデンス分析』高橋信(オーム社)198ページ
とのこと。
これは、カイ二乗検定で信頼性のある結果を得るためには、
- サンプルサイズが20を下回らない
- 期待度数が5未満のマス目が20%以上にならない
というコクラン・ルールを、2×2表という条件下で説明したもののようです。
よって、3行以上または3列以上であっても、このルールに抵触してしまう場合は、カイ二乗検定を使うことは適切ではない、ということになります。
フィッシャーの正確検定も、2×2表に限らず、3行以上または3列以上であってもP値を求めることができるそうです *1 が、行または列が増えるごとに計算工程が膨大になっていくせいか、市販の統計ソフトであっても2×2表のみ可としていたりします。
そこで今回は、Excel VBAで2×2表専用のフィッシャーの正確検定ができる関数を作ってみました。
ソースコード
'フィッシャーの正確検定(2×2表専用)
Function Exact_Test(参照, Optional 検定の指定)
Dim Acu, Min, Std, Tmp, Arr(1 To 4)
Dim i, j, k, l
If IsMissing(検定の指定) Then 検定の指定 = 1
If 参照.Rows.Count = 2 And 参照.Columns.Count = 2 _
And (検定の指定 = 1 Or 検定の指定 = 2) Then
For i = 1 To 4
Arr(i) = 参照(i)
Next
With Application.WorksheetFunction
Min = .Min(Arr)
For i = 1 To 4
If Arr(i) = Min Then Exit For
Next
Select Case i
Case 1: j = 2: k = 3: l = 4
Case 2: j = 1: k = 4: l = 3
Case 3: j = 1: k = 4: l = 2
Case 4: j = 2: k = 3: l = 1
End Select
Std = (.Fact(Arr(1) + Arr(2)) * .Fact(Arr(3) + Arr(4)) _
* .Fact(Arr(1) + Arr(3)) * .Fact(Arr(2) + Arr(4))) _
/ (.Fact(Arr(1)) * .Fact(Arr(2)) * .Fact(Arr(3)) _
* .Fact(Arr(4)) * .Fact(.Sum(Arr)))
Do
Acu = Acu + (.Fact(Arr(1) + Arr(2)) * .Fact(Arr(3) + Arr(4)) _
* .Fact(Arr(1) + Arr(3)) * .Fact(Arr(2) + Arr(4))) _
/ (.Fact(Arr(1)) * .Fact(Arr(2)) * .Fact(Arr(3)) _
* .Fact(Arr(4)) * .Fact(.Sum(Arr)))
If Arr(i) = 0 Then Exit Do
Arr(i) = Arr(i) - 1
Arr(j) = Arr(j) + 1
Arr(k) = Arr(k) + 1
Arr(l) = Arr(l) - 1
Loop
If 検定の指定 = 2 Then
If Arr(j) < Arr(k) Then
Tmp = Arr(j)
Else
Tmp = Arr(k)
End If
Arr(i) = Arr(i) + Tmp
Arr(j) = Arr(j) - Tmp
Arr(k) = Arr(k) - Tmp
Arr(l) = Arr(l) + Tmp
Do
Tmp = (.Fact(Arr(1) + Arr(2)) * .Fact(Arr(3) + Arr(4)) _
* .Fact(Arr(1) + Arr(3)) * .Fact(Arr(2) + Arr(4))) _
/ (.Fact(Arr(1)) * .Fact(Arr(2)) * .Fact(Arr(3)) _
* .Fact(Arr(4)) * .Fact(.Sum(Arr)))
If Tmp > Std Then Exit Do
Acu = Acu + Tmp
Arr(i) = Arr(i) - 1
Arr(j) = Arr(j) + 1
Arr(k) = Arr(k) + 1
Arr(l) = Arr(l) - 1
Loop
End If
Exact_Test = Acu
End With
Else
Exact_Test = CVErr(xlErrNA)
End If
End Function
コードの解説
ここは、ウィキペディアの以下の説明を参考にしました。
p値を求めるには(普通の検定と同じように)実際の観測データよりも極端な場合も含めて考えなければならない。フィッシャーは、そのためには小計が観測値と同じになるような場合だけを考慮すればよいことを示した。今の例では、その考慮に入れるべき場合は次の1つ(男はだれもダイエットしていない)だけである