H0の下でブートストラップを使用して、2つの手段の違いのテストを実行します。グループ内またはプールされたサンプル内の置換


18

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つの手段を比較することを目的とした投稿質問で、ブートストラップの異常な結果を説明しました。ただし、その投稿では、ブートストラップの前にデータが変更されたときにブートストラップを実行する方法については説明していません。ブートストラップの前にデータが変更されるアプローチは次のようになります。

  1. H0は、2つのグループの平均が同じであることを示します
  2. プールされたサンプルの平均から個々の観測値を減算すると、H0が成り立ちます。

この場合、データの変更はグループの平均に影響するため、グループ内(およびグループ間)の差異ではなく、その差に影響します。

  1. 変更されたデータは、各グループ内で個別にサンプリングが実行されるという注意事項とともに、さらなるブートストラップの基礎となります。
  2. g1とg2のブートストラップ平均の差が計算され、グループ間で観測された(変更されていない)差と比較されます。
  3. 観察された値を反復数で割った値と等しいかそれ以上の極値の割合は、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の説明に対処しなければならないと思いますが、どこが鍵なのかわかりません...


1
興味深い問題とうまく提示。ブートストラップには、元のサンプルが母集団をあまりよく表していない可能性が高いために、サンプルサイズが非常に小さい場合に問題があります。サンプルサイズは、ブートストラップが機能するために非常に大きくする必要はありません。サンプルサイズ6と22はそれほど悪くないかもしれません。Efron(1983)の論文で、ブートストラップは、各トレーニングサンプルサイズが10未満である2クラスの分類問題における線形判別関数のエラー率を推定するためのCVの形式と比較されました。 2007)。
マイケルR.チェルニック

2
私の本には、ブートストラップがいつ失敗するかに関する章もあり、小さなサンプルサイズの問題に関する議論が含まれています。
マイケルR.チェルニック

単一あなたはそれを動作させるためにあなたのブートストラップ#2のアプローチに修正する必要がある行は次のいずれかですH0 <- pool - mean (pool)。に置き換える必要がありますH0 <- c(g1.lengths - mean(g1.lengths), g2.lengths - mean(g2.lengths))。その後、0.0023のp値を取得します。(これはZenitが答えで説明したものと同じです。)これですべてです。コードの単純なバグです。CCから@MichaelChernick
amoebaによると

これらの方法は圧倒されますか?グループが非常に大きい場合、プール> 43kの場合、重要な差として任意の差を検出できるかどうかを意味します。
アレックスアルバレスペレス

回答:


17

Efron's and Tibshirani's An Introduction to the bootstrap(220-224ページ)の第16章に基づいて、これについての私の見解を示します。短いのは、2番目のブートストラップアルゴリズムが誤って実装されていることですが、一般的な考え方は正しいです。

ブートストラップテストを実施する場合、リサンプリングメソッドが帰無仮説に対応するデータを生成することを確認する必要があります。この投稿を説明するために、Rのスリープデータを使用します。教科書で推奨されている手段の違いだけではなく、学生ごとの検定統計量を使用していることに注意してください。

分析結果を使用してt統計のサンプリング分布に関する情報を取得する従来のt検定では、次の結果が得られます。

x <- sleep$extra[sleep$group==1] y <- sleep$extra[sleep$group==2]
t.test(x,y)
t = -1.8608, df = 17.776, p-value = 0.07939

n1n2

# pooled sample, assumes equal variance
pooled <- c(x,y)
for (i in 1:10000){
  sample.index <- sample(c(1:length(pooled)),replace=TRUE)
  sample.x <- pooled[sample.index][1:length(x)]
  sample.y <- pooled[sample.index][-c(1:length(y))]
  boot.t[i] <- t.test(sample.x,sample.y)$statistic
}
p.pooled <-  (1 + sum(abs(boot.t) > abs(t.test(x,y)$statistic))) / (10000+1) 
p.pooled
[1] 0.07929207

H0H0H0z¯

バツ=バツバツ¯+z¯
y=yy¯+z¯

バツ/yz¯H0

# sample from H0 separately, no assumption about equal variance
xt <- x - mean(x) + mean(sleep$extra) #
yt <- y - mean(y) + mean(sleep$extra)

boot.t <- c(1:10000)
for (i in 1:10000){
  sample.x <- sample(xt,replace=TRUE)
  sample.y <- sample(yt,replace=TRUE)
  boot.t[i] <- t.test(sample.x,sample.y)$statistic
}
p.h0 <-  (1 + sum(abs(boot.t) > abs(t.test(x,y)$statistic))) / (10000+1)  # 
p.h0
[1] 0.08049195

今回は、3つのアプローチで同様のp値になりました。お役に立てれば!


1
親切にして、以下に「1」が追加された理由を説明してください(1 + sum(abs(boot.t) > abs(t.test(x,y)$statistic))) / (10000+1):このようなものの代わりに:mean(abs(boot.t) > abs(t.test(x,y)$statistic))時間をありがとう。
TG_Montana

+1。このbootstrap-on-modified-data-to-sample-from-H0アプローチには特定の名前がありますか?
アメーバは、モニカを復活させる

3
H0pvalあなたはe=何度か {t>tobs}BB

@amoeba:この手続きに正式な名前があるかどうかは定かではありませんが、distributionsではなくmeansの等価性のブートストラップテストとして説明できると思います。完全な手順を示すページはgoogle booksにはありませんが、その動機は223ページにあります。別の説明は、13ページのこれらのメモにあります(galton.uchicago.edu/~eichler/stat24600/Handouts/bootstrap。 pdf)。
ゼニット

(+1)すばらしい答え。「このアルゴリズム[センタリングせずにデータ自体をリサンプリングする]が実際にxとyの分布が同一であるかどうかをテストしている」理由について詳しく説明してください。このリサンプリング戦略、分布が同じであること保証することを理解していますが、なぜそれらが同じであることをテストするのですか?
ハーフ渡す
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.