2つの独立したグループを持つデータがあるとします。
g1.lengths <- c (112.64, 97.10, 84.18, 106.96, 98.42, 101.66)
g2.lengths <- c (84.44, 82.10, 83.26, 81.02, 81.86, 86.80,
85.84, 97.08, 79.64, 83.32, 91.04, 85.92,
73.52, 85.58, 97.70, 89.72, 88.92, 103.72,
105.02, 99.48, 89.50, 81.74)
group = rep (c ("g1", "g2"), c (length (g1.lengths), length (g2.lengths)))
lengths = data.frame( lengths = c(g1.lengths, g2.lengths), group)
g1に6つの観測値があり、g2に22の観測値がある場合、グループごとのサンプルサイズにバイアスがかかっていることは明らかです。従来のANOVAでは、臨界値が0.05(p値は0.0044)に設定されている場合、グループの平均値は異なることを示唆しています。
summary (aov (lengths~group, data = lengths))
私の目的は平均差を比較することであるため、このような不均衡で小さなサンプルデータは、従来のアプローチでは不適切な結果をもたらす可能性があります。したがって、置換テストとブートストラップを実行したいと思います。
授精テスト
帰無仮説(H0)は、グループの平均が同じであると述べています。順列検定におけるこの仮定は、グループを1つのサンプルにプールすることにより正当化されます。これにより、2つのグループのサンプルが同一の分布から抽出されたことが保証されます。プールされたデータから繰り返しサンプリング(より正確には-シャッフル)することにより、観測値は新しい方法でサンプルに再割り当て(シャッフル)され、検定統計量が計算されます。これをn回実行すると、H0がTRUEであるという仮定の下で、テスト統計のサンプリング分布が得られます。最後に、H0の下で、p値は、検定統計量が観測値以上になる確率です。
s.size.g1 <- length (g1.lengths)
s.size.g2 <- length (g2.lengths)
pool <- lengths$lengths
obs.diff.p <- mean (g1.lengths) - mean (g2.lengths)
iterations <- 10000
sampl.dist.p <- NULL
set.seed (5)
for (i in 1 : iterations) {
resample <- sample (c(1:length (pool)), length(pool))
g1.perm = pool[resample][1 : s.size.g1]
g2.perm = pool[resample][(s.size.g1+1) : length(pool)]
sampl.dist.p[i] = mean (g1.perm) - mean (g2.perm)
}
p.permute <- (sum (abs (sampl.dist.p) >= abs(obs.diff.p)) + 1)/ (iterations+1)
順列検定の報告されたp値は0.0053です。OK、正しくやれば、順列とパラメーターANOVAはほぼ同じ結果をもたらします。
ブートストラップ
まず、サンプルサイズが小さすぎるとブートストラップが役に立たないことを認識しています。この投稿は、それがさらに悪くて誤解を招くことができることを示しました。また、2番目は、仮説検定が主な目的である場合、順列検定は一般にブートストラップよりも優れていることを強調しました。それにもかかわらず、この素晴らしい投稿では、コンピューターを集中的に使用する方法の重要な違いに対処しています。しかし、ここで私は別の質問を提起したい(信じている)。
最も一般的なブートストラップアプローチを最初に紹介します(Bootstrap1:プールされたサンプル内のリサンプリング):
s.size.g1 <- length (g1.lengths)
s.size.g2 <- length (g2.lengths)
pool <- lengths$lengths
obs.diff.b1 <- mean (g1.lengths) - mean (g2.lengths)
iterations <- 10000
sampl.dist.b1 <- NULL
set.seed (5)
for (i in 1 : iterations) {
resample <- sample (c(1:length (pool)), length(pool), replace = TRUE)
# "replace = TRUE" is the only difference between bootstrap and permutations
g1.perm = pool[resample][1 : s.size.g1]
g2.perm = pool[resample][(s.size.g1+1) : length(pool)]
sampl.dist.b1[i] = mean (g1.perm) - mean (g2.perm)
}
p.boot1 <- (sum (abs (sampl.dist.b1) >= obs.diff.b1) + 1)/ (iterations+1)
この方法で実行されるブートストラップのP値は0.005です。これは合理的で、パラメトリックANOVAおよび置換テストとほとんど同じように聞こえますが、後続のサンプルを抽出したサンプルをプールしただけで、このブートストラップでH0を正当化するのは適切ですか?
いくつかの科学論文で見つけた異なるアプローチ。具体的には、ブートストラップの前にH0を満たすために研究者がデータを変更するのを見ました。周りを検索すると、CVで非常に興味深い投稿が見つかりました。@ jan.sは、2つの手段を比較することを目的とした投稿質問で、ブートストラップの異常な結果を説明しました。ただし、その投稿では、ブートストラップの前にデータが変更されたときにブートストラップを実行する方法については説明していません。ブートストラップの前にデータが変更されるアプローチは次のようになります。
- H0は、2つのグループの平均が同じであることを示します
- プールされたサンプルの平均から個々の観測値を減算すると、H0が成り立ちます。
この場合、データの変更はグループの平均に影響するため、グループ内(およびグループ間)の差異ではなく、その差に影響します。
- 変更されたデータは、各グループ内で個別にサンプリングが実行されるという注意事項とともに、さらなるブートストラップの基礎となります。
- g1とg2のブートストラップ平均の差が計算され、グループ間で観測された(変更されていない)差と比較されます。
- 観察された値を反復数で割った値と等しいかそれ以上の極値の割合は、p値を与えます。
コードは次のとおりです(Bootstrap2:H0がTRUEである変更後のグループ内でのリサンプリング):
s.size.g1 <- length (g1.lengths)
s.size.g2 <- length (g2.lengths)
pool <- lengths$lengths
obs.diff.b2 <- mean (g1.lengths) - mean (g2.lengths)
# make H0 to be true (no difference between means of two groups)
H0 <- pool - mean (pool)
# g1 from H0
g1.H0 <- H0[1:s.size.g1]
# g2 from H0
g2.H0 <- H0[(s.size.g1+1):length(pool)]
iterations <- 10000
sampl.dist.b2 <- NULL
set.seed (5)
for (i in 1 : iterations) {
# Sample with replacement in g1
g1.boot = sample (g1.H0, replace = T)
# Sample with replacement in g2
g2.boot = sample (g2.H0, replace = T)
# bootstrapped difference
sampl.dist.b2[i] <- mean (g1.boot) - mean (g2.boot)
}
p.boot2 <- (sum (abs (sampl.dist.b2) >= obs.diff.b2) + 1)/ (iterations+1)
そのような実行されたブートストラップは、以前のテストと比較して非常に異なる0.514の p値を提供します。これは@ jan.sの説明に対処しなければならないと思いますが、どこが鍵なのかわかりません...
H0 <- pool - mean (pool)
。に置き換える必要がありますH0 <- c(g1.lengths - mean(g1.lengths), g2.lengths - mean(g2.lengths))
。その後、0.0023のp値を取得します。(これはZenitが答えで説明したものと同じです。)これですべてです。コードの単純なバグです。CCから@MichaelChernick