使用H0下的引导程序来测试两种方法之间的差异:在组内还是在合并样本中进行替换


18

假设我有一个包含两个独立组的数据:

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 观测值。传统的方差分析表明,当临界值设置为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。好的,如果我做对了,排列和参数方差分析将得出几乎相同的结果。

自举

首先,我知道当样本量太小时,引导程序无济于事。这篇文章表明,情况可能更糟,而且会产生误导。另外,第二篇文章强调了以假设检验为主要目标时,置换检验通常比自举更好。尽管如此,这篇出色的文章解决了计算机密集型方法之间的重要差异。但是,我想在这里提出(我相信)一个不同的问题。

让我首先介绍最常见的引导方法(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在帖子问题中解释了引导程序的异常结果,目的是比较两种方法。但是,在那篇文章中没有介绍在引导之前修改数据时如何执行引导。引导之前修改数据的方法如下所示:

  1. H0表示两组的均值相同
  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)的论文中,将引导程序与一种CV形式进行了比较,以估计2种类别的分类问题中线性判别函数的错误率,每类训练样本的大小均小于10。 2007)。
Michael R. Chernick

2
我的书中还有一章介绍引导程序何时失败,并讨论了小样本量问题。
Michael R. Chernick

行,你需要修复您的引导#2的方法,使其工作是这个:H0 <- pool - mean (pool)。需要将其替换H0 <- c(g1.lengths - mean(g1.lengths), g2.lengths - mean(g2.lengths))。然后,您将获得0.0023的p值。(这与Zenit在他的回答中解释的是同一件事。)这就是全部,只是代码中的一个简单错误。CC致@MichaelChernick
变形虫说莫妮卡(Monica)恢复

这些方法会被压倒吗?我的意思是,当组非常大时:池> 43k,他们是否可以检测到任何明显的差异。
Alex AlvarezPérez18年

Answers:


17

这是我的看法,它是基于Efron和Tibshirani的“ bootstrap简介”(第220-224页)的第16章。简而言之,您的第二个引导算法执行有误,但总体思路是正确的。

在进行自举测试时,必须确保重新采样方法生成的数据与原假设相对应。我将使用R中的sleep数据来说明这篇文章。请注意,我使用的是学生化的测试统计数据,而不仅仅是教科书建议的均值差异。

经典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¯

x~i=xix¯+z¯
y~i=yiy¯+z¯

x~/y~z¯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

这次,我们得出了三种方法的相似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

+1。这种从H0修改数据到样本的引导方法是否具有特定名称?
变形虫说莫妮卡(Monica)恢复职权

3
H0pvalue=number of times {t>tobs}BB

@amoeba:我不确定此过程是否具有正式名称或商定名称,但我想它可以描述为均值相等而不是分布均的自举测试Google图书缺少显示完整过程的页面,但其动机显示在223页上。有关该过程的另一种说明,请参见第13页的这些说明(galton.uchicago.edu/~eichler/stat24600/Handouts/bootstrap。 pdf)。
Zenit

(+1)个极好的答案。您能否详细说明为什么“此算法[对数据本身进行采样而不进行居中]实际上是在测试x和y的分布是否相同”?我知道这种重采样策略可确保分布相同,但是为什么要测试它们是否相同?
半通
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.