经典假设检验
这章解决的基本问题是,在一个样本中观察到的效应是否也会出现在更大规模的总体中。例如,在NSFG样本中,第一胎和其他胎的妊娠期长度不同,这种效应是真实反映了美国妇女的生育情况,还是偶然出现在这个样本中而已。
这个问题有几种表示方法:Fisher原假设检验、Neyman-Pearson决策理论和贝叶斯推理,大部分人在实践中使用的都是这3中方法。这里介绍这些方法的一个子集,称为经典假设检验(classical hypothesis testing)。
经典假设检验的目的是回答一个问题:“给定一个样本和一个直观效应,这个效应是偶然出现的概率为多少?”,回答这个问题的步骤如下:
第一步,选择一个检验统计量(test statistic),对直观效应进行量化。
第二步,定义原假设(null hypothesis)。原假设是系统的一个模型,所基于的假设是直观效应不为真。
第三步,计算p值。p值是在原假设为真时,直观效应出现的概率。
最后,解释结果。如果p值很低,我们称这个效应是统计显著(statistically significant)的,即不太可能偶然发生。在这种情况下,我们推断,这个效应在大规模总体中出现的可能性更大。
假设检验
本文用HypothesisTest表示一个经典假设检验结果,定义如下:
class HypothesisTest(object):
def __init__(self, data):
self.data = data
self.MakeModel()
self.actual = self.TestStatistic(data)
def PValue(self, iters=1000):
self.test_stats = [self.TestStatistic(self.RunModel())
for _ in range(iters)]
count = sum(1 for x in self.test_stats if x >= self.actual)
return count / iters
def TestStatistic(self, data):
raise UnimplementedMethodException()
def MakeModel(self):
pass
def RunModel(self):
raise UnimplementedException()HypothesisTest是一个抽象父类,完整定义了一些方法,并为其他方法预留接口。基于HypothesisTest的子类继承了__init__和PValue,实现了TestsStatistic和RunModel,可选择是否定义MakeModel。
举个简单的例子,假设投掷一枚硬币250次,结果得到140次正面和110次反面。基于这个结果,我们怀疑这个硬币质地不均匀,落地时正面朝上的可能性更大。为了检验这个假设,我们计算出当质地均匀时出现这种结果的概率。
class CoinTest(HypothesisTest):
def TestStatistic(self, data):
heads, tails = data
test_stat = abs(heads - tails)
return test_stat
def RunModel(self):
heads, tails = self.data
n = heads + tails
sample = [random.choice('HT') for _ in range(n)]
hist = Hist(sample)
data = hist['H'], hist['T']
return data参数data是一对整数,即结果为正面和反面的次数。检验统计量是两者的差值30。计算PValue的结果为0.07,也就是如果硬币质地均匀的,预期有7%的可能性看到正面和反面的差值达到30的情况。
如果p值小于1%,那么效应不太可能是随机产生的;如果p值大于10%,那么效应可以合理解释为随机现象。位于1%和10%之间的p值应看作边缘值。
检验均值差
在全国家庭增长调查数据中,我们看到第一胎比其他胎的妊娠时间稍长,出生体重略轻。现在,我们要看看这些效应是否统计显著。
这些例子的原假设是两组样本的分布相同。对这个原假设建模,一个方法是置换(permutation),即从两组中取值混排,把两个组当成一个大组。
class DiffMeansPermute(HypothesisTest):
def TestStatistic(self, data):
group1, group2 = data
test_stat = abs(group1.mean() - group2.mean())
return test_stat
def MakeModel(self):
group1, group2 = self.data
self.n, self.m = len(group1), len(group2)
self.pool = np.hstack((group1, group2))
def RunModel(self):
np.random.shuffle(self.pool)
data = self.pool[:self.n], self.pool[self.n:]
return datadata是一对序列,每组一个序列。检验统计量是两组序列的均值差。
我们将妊娠时间数据抽取为NumPy数组,传递给DiffMeansPermute,计算出p值,计算结果约为0.17,即我们预期有17%的可能性看到妊娠时间差达到所观测的差值。因此,这个效应不是统计显著的。
图1 原假设下,妊娠期时间均值差CDF
检验相关性
在全国家庭增长调查数据集中,新生儿体重和母亲年龄的相关性约为0.07。年龄较大的母亲似乎产下的孩子更重,但这种效应是偶然产生的吗?
我们选择Pearson相关性作为检验统计量,但Spearman相关性也是很好的选择。如果我们有理由预期正相关,那么就可以进行单侧检验。由于我们并没有任何相关证据,还是选择使用相关性的绝对值进行双侧检验。
检验的原假设是母亲年龄和新生儿体重之间没有相关性。我们可以将观察值混排进行模拟,在这个模拟世界中,母亲年龄和新生儿体重的分布仍保持不变,但这两个变量之间没有相关性。
class CorrelationPermute(HypothesisTest):
def TestStatistic(self, data):
xs, ys = data
test_stat = abs(Corr(xs, ys))
return test_stat
def RunModel(self):
xs, ys = self.data
xs = np.random.permutation(xs)
return xs, ysdata是一对序列,TestStatistic计算Pearson相关性的绝对值,RunModel将xs进行混排,返回模拟数据。
实际数据的相关性为0.07,检验计算得到的p值为0。在1000次重复实验中,模拟得到的最大相关性为0.04。因此,虽然观察到的变量相关性很小,但这种相关性是统计显著的。
检验比例
假设你怀疑一个骰子有问题,将这个骰子掷了60次,得到如下结果:
你希望的结果是每个点数平均出现10次。在这个数据集中,3出现的次数较多,4较少。但是,这个差异是统计显著的吗?
为了计算这个假设,我们可以计算出每个值的预期频数、预期频数与观察频数的差值,以及差值绝对值的和。在这个示例中,差值绝对值的和为20。完全偶然出现这么大差值的概率是多少呢?
class DiceTest(HypothesisTest):
def TestStatistic(self, data):
observed = data
n = sum(observed)
expected = np.ones(6) * n /6
test_stat = sum(abs(observed - expected))
return test_stat
def RunModel(self):
n = sum(self.data)
values = [1, 2, 3, 4, 5, 6]
rolls = np.random.choice(values, n ,replace=True)
hist = Hist(rolls)
freqs = hist.Freqs(values)
return freqs上述代码中使用的数据表示一列频数,观察值为[8, 9, 19, 5, 8, 11],预期频数都是10。检验统计量是差值绝对值的和。
原假设是骰子没有问题,因此从values中随机抽取样本进行模拟。计算得到的p值为0.13,因此,这个直观效应不是统计显著的。
卡方检验
前面,我们使用偏差总和作为检测统计量。但是,检测比例时,人们更多使用的是卡方统计量。
其中Oi是观察到的频数,Ei是预期频数。
使用卡方统计量计算的p值为0.04,明显小于使用偏差和的值0.13。将这两个检验放在一起考虑,无法排除骰子有问题的可能性,也不能肯定骰子一定有问题。
这个示例说明了一个重要问题:p值取决于检验统计量的选择和原假设模型,有时这些因素决定了一个效应是否统计显著。
卡方检验(chi-squared test)可以证明两个群组之间存在差异,但不能揭示这个差异是什么。
误差
在经典假设检验中,如果p值低于某个阈值(常用阈值为5%),那么我们就认为一个效应是统计显著的。这个过程产生两个问题:
如果一个效应的确是偶然产生的,那么我们将它误判为统计显著的概率是多少?这个概率就是误报率(false positive rate)。
如果一个效应不是偶然的,那么假设检验失败的概率是多少?这个概率称为漏报率(false negative rate).
功效
误报率受实际效应大小的影响,而通常我们无法得知实际效应的大小,因此误报率较难计算。一个办法是计算一个假定效应大小的误报率。
我们以妊娠时间计算进行模拟,计算结果为:如果妊娠时间均值的实际差异为0.78周,那么我们预期如果使用整个规模的样本就行实验,结果有70%的可能性为误报。
人们经常使用另一种方式描述这个结果:如果实际差异为0.78周,那么我们预期检验通过的可能性只有30%。这个“正确通过率”称为检验的功效,有时也称为“敏感度”。这个值反映了一个检验检测出指定大小效应的能力。
通常,假设检验失败并不说明两个群组之间不存在差异,而是说,如果差异的确存在的话,这个差异太小,以至于无法在这种规模的样本中检测到。
参考文献:
统计思维. Allen B.Downey. 金迎 译