假设我有一个包含两个独立组的数据:
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) 表明组的均值相同。通过将组合并到一个样本中来证明置换检验中的这一假设是合理的。这确保了两组的样本来自相同的分布。通过从合并数据中重复采样(或更准确地说 - 重新洗牌),观察以新的方式重新分配(洗牌)到样本,并计算测试统计量。执行 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。好的,如果我做得正确,排列和参数方差分析会给出几乎相同的结果。
引导程序
首先,我知道当样本量太小时,bootstrap 无济于事。这篇文章表明它可能会更糟糕和误导。此外,第二个强调当假设检验是主要目标时,置换检验通常比引导法更好。尽管如此,这篇伟大的文章解决了计算机密集型方法之间的重要差异。然而,在这里我想提出(我相信)一个不同的问题。
让我首先介绍最常见的引导方法(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。即使这听起来很合理并且与参数方差分析和置换检验几乎相同,基于我们刚刚合并从中抽取后续样本的样本来证明这个引导程序中的 H0 是否合适?
我在几篇科学论文中发现了不同的方法。具体来说,我看到研究人员修改数据以在引导之前满足 H0。环顾四周,我在 CV中发现了非常有趣的帖子, @jan.s在帖子问题中解释了引导程序的异常结果,其目的是比较两种方法。但是,在那篇文章中没有介绍在引导之前修改数据时如何执行引导。在引导之前修改数据的方法如下所示:
- H0 表示两组的均值相同
- 如果我们从合并样本的平均值中减去单个观察值,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'的解释,但我无法弄清楚关键在哪里......