← 返回《数据科学的统计基础》
📑 查看全课大纲(第 36 / 41 节)

符号检验

约 25 分钟

📺 正在播放小象官方高清录播(支持倍速与清晰度调节)

符号检验

小象实战讲义 · 数据科学的统计基础

在数据科学实践中,我们常常需要对总体的某些特征进行推断,例如判断一个城市的生活成本是否高于全球平均水平,或比较两种实验方法的效果是否有差异。然而,传统的参数检验(如t检验)通常要求数据来自特定的分布(如正态分布)。当数据分布未知或存在异常值时,这些方法可能失效。本节介绍的符号检验,作为一种经典的非参数检验方法,仅依赖于数据的“符号”(正或负),对总体分布形式不做任何假定,为我们提供了一种稳健的推断工具。学完本节,你将能够使用符号检验来检验总体的中位数,并比较成对样本的差异。

💡 核心导读

  • 核心思想:符号检验的核心是检验总体分布的中位数是否等于某个特定值,或者两个总体的中位数是否有差异。它仅利用样本观测值与比较值之差的符号(正或负)进行推断。
  • 检验统计量:构造一个计数统计量BB,表示样本中大于比较值的个数。在原假设成立时,BB服从二项分布B(n,0.5)B(n, 0.5)
  • 拒绝域的确定:根据二项分布的单峰特性,拒绝域通常位于分布的两侧尾部,通过控制显著性水平α\alpha来找到临界值c1c_1c2c_2
  • 两大应用场景:1) 检验单个样本的中位数是否等于某个值;2) 基于成对样本,检验两个总体的中位数是否存在显著差异。
  • Python实现:使用scipy.stats中的binomtest函数可以方便地完成符号检验的计算与推断。

符号检验的基本原理

符号检验是一种最简单的非参数检验方法。其基本思想是:当我们关心总体的某个位置特征(通常是中位数)时,可以忽略样本观测值的具体数值大小,只关注它们相对于某个参考值的符号(正号或负号)。

问题的提出

X1,X2,,XnX_1, X_2, \ldots, X_n是从总体中抽取的一组独立同分布的随机样本,其分布函数为F(x)F(x),并假设F(x)F(x)x=0x=0处连续。

我们考虑如下假设检验问题:

  • 原假设H0H_0F(0)=0.5F(0) = 0.5
  • 备择假设H1H_1F(0)0.5F(0) \neq 0.5

这个假设检验的统计含义是什么?回忆分布函数的定义,F(0)=P(X0)F(0) = P(X \le 0)。因此,F(0)=0.5F(0) = 0.5意味着P(X0)=0.5P(X \le 0) = 0.5,同时也意味着P(X>0)=0.5P(X > 0) = 0.5。这正是总体中位数等于0的数学表述。因此,上述检验问题等价于:

  • H0H_0:总体中位数M=0M = 0
  • H1H_1:总体中位数M0M \neq 0

更一般地,如果我们想检验总体中位数MM是否等于某个常数M0M_0,只需将样本进行平移,令Yi=XiM0Y_i = X_i - M_0,然后检验YY的中位数是否为0即可。

检验统计量与分布

为了检验H0H_0,我们构造一个计数统计量BBB=i=1nI(Xi>0)B = \sum_{i=1}^{n} I(X_i > 0) 其中I()I(\cdot)为示性函数,当括号内条件成立时取值为1,否则为0。因此,BB表示nn个样本X1,,XnX_1, \ldots, X_n中取正号的个数。

在原假设H0:M=0H_0: M=0成立的条件下,每个样本XiX_i大于0的概率为p=P(Xi>0)=0.5p = P(X_i > 0) = 0.5,小于0的概率也为0.50.5。由于样本是独立同分布的,每个样本是否大于0可以看作一次独立的伯努利试验(成功定义为Xi>0X_i > 0)。因此,BB就是nn次独立伯努利试验中成功的次数,它服从参数为(n,0.5)(n, 0.5)的二项分布: BB(n,0.5)H0下成立.B \sim B(n, 0.5) \quad \text{在} H_0 \text{下成立}.

拒绝域的确定

由于备择假设H1:M0H_1: M \neq 0是双边的,我们采用双侧检验。二项分布B(n,0.5)B(n, 0.5)的分布律大致呈单峰形状,取值在中间(如n/2n/2附近)的概率较大,在两端(接近0或nn)的概率较小。

因此,拒绝域WW应取在分布的两侧尾部,形式为: W={Bc1}{Bc2}W = { B \le c_1 } \cup { B \ge c_2 } 其中临界值c1c_1c2c_2由显著性水平α\alpha决定,通常要求: PH0(Bc1)α/2PH0(Bc2)α/2.P_{H_0}(B \le c_1) \le \alpha/2 \quad \text{且} \quad P_{H_0}(B \ge c_2) \le \alpha/2. 实际操作中,我们通常计算检验的p值进行判断。对于观测到的统计量值bobsb_{obs},双侧检验的p值为: p-value=2×min{PH0(Bbobs), PH0(Bbobs)}.p\text{-value} = 2 \times \min \left{ P_{H_0}(B \le b_{obs}),\ P_{H_0}(B \ge b_{obs}) \right}. 如果p值小于给定的显著性水平α\alpha,则拒绝原假设。

符号检验的应用

符号检验因其简单和稳健性,在多个场景中都有应用。下面介绍两个主要应用场景及其Python实现。

应用一:检验单个样本的中位数

这是符号检验最直接的应用。我们想判断样本所来自的总体的中位数是否等于某个预设值M0M_0

检验步骤

  1. 数据变换:将每个样本观测值减去M0M_0,得到Yi=XiM0Y_i = X_i - M_0
  2. 提出假设:检验YY的中位数是否为0。
    • H0H_0M=M0M = M_0 (等价于MY=0M_Y = 0
    • H1H_1MM0M \ne M_0 (或M>M0M > M_0, M<M0M < M_0
  3. 计算统计量:计算B=i=1nI(Yi>0)B = \sum_{i=1}^{n} I(Y_i > 0),即YiY_i中正号的个数。
  4. 统计推断:在H0H_0下,BB(n,0.5)B \sim B(n, 0.5)。根据BB的观测值计算p值并做出决策。

实例分析:城市生活成本排名

假设联合国统计了全球66个大城市的生活花费指数(以纽约1996年12月为100基准)。北京的数据为99。我们想知道,北京的生活花费指数是在全球城市的中位数之上还是之下?

  • 问题转化:设全球大城市生活花费指数的中位数为MM
    • 若北京在中位数之下,则意味着M99M \ge 99
    • 若北京在中位数之上,则意味着M<99M < 99
  • 假设设置:我们可以将问题转化为检验M99M \ge 99是否成立。为了使用符号检验,我们通常检验M=99M = 99,备择假设为M<99M < 99
    • H0:M99H_0: M \ge 99 (或等价地,检验M=99M = 99,备择为M<99M < 99
    • H1:M<99H_1: M < 99

下面我们用Python的scipy.stats模块来实现这个检验。首先,我们模拟一组66个城市的生活花费指数数据(为了示例,数据已按从小到大排序,北京99位于其中)。

import numpy as np
from scipy.stats import binomtest

# 设置随机种子保证结果可复现
np.random.seed(321)

# 模拟66个城市的生活花费指数数据(已排序,范围大致在80-120之间)
# 为了构造一个中位数明显不等于99的示例,我们让数据整体偏高
data = np.random.normal(loc=105, scale=10, size=66)  # 均值为105的正态分布
data = np.sort(data)  # 排序以模拟排名数据
# 确保数据中有99这个值(模拟北京的数据点)
# 在实际案例中,data是给定的,这里我们直接使用模拟数据进行分析。

print(f"模拟数据的前5个值: {data[:5].round(2)}")
print(f"模拟数据的后5个值: {data[-5:].round(2)}")
print(f"模拟数据的中位数: {np.median(data):.2f}")

# 进行符号检验:检验中位数 M 是否等于 99,备择假设为 M < 99
# binomtest 参数:
# k: 大于比较值(M0)的样本个数,即成功次数
# n: 总试验次数(样本量)
# p: 原假设下的成功概率,默认为0.5
# alternative: 备择假设方向,'two-sided', 'greater', 'less'

M0 = 99
# 计算大于M0的样本个数
k = np.sum(data > M0)
n = len(data)

print(f"\n样本量 n = {n}")
print(f"大于{M0}的样本个数 k = {k}")

# 执行二项检验(符号检验)
# 原假设 H0: 数据中位数等于99 (即 p = P(X>99) = 0.5)
# 备择假设 H1: 数据中位数小于99 (即 p = P(X>99) < 0.5)
res = binomtest(k, n, p=0.5, alternative='less')

print(f"\n符号检验结果:")
print(f"统计量 (成功次数 k): {res.statistic}")
print(f"p值: {res.pvalue:.6f}")
print(f"成功概率的估计值 (k/n): {res.proportion_estimate:.4f}")
print(f"成功概率的95%置信区间: {res.proportion_ci(confidence_level=0.95)}")

# 决策 (显著性水平 alpha = 0.05)
alpha = 0.05
if res.pvalue < alpha:
    print(f"\n结论: 在{alpha}的显著性水平下,拒绝原假设(H0: M >= 99)。")
    print("这意味着有充分证据表明,总体中位数小于99。")
    print("结合背景,北京的生活花费指数位于全球城市中位数之上。")
else:
    print(f"\n结论: 在{alpha}的显著性水平下,没有充分证据拒绝原假设。")
    print("不能认为总体中位数小于99。")

在这个模拟的例子中,由于我们生成的数据中心在105,远大于99,数据强烈支持”中位数大于等于99”的原假设,因此检验结果是不拒绝原假设(p值约为0.9999,非常接近1)。这也提示了一个实用的判读技巧:当 p 值接近 1 时,往往说明备择假设的方向选反了。若把备择假设改为 M>99M > 99alternative='greater'),同样的数据会得到远小于0.05的p值(约0.0002),从而拒绝原假设,得出北京生活花费指数高于99(全球中位数水平)的结论。这展示了符号检验如何将实际问题转化为对二项分布参数的检验,也说明了单侧检验中备择假设方向的重要性。

应用二:基于成对样本的比较

符号检验的另一个重要应用是处理成对样本数据,用于检验两个相关总体的中位数是否存在差异。例如,比较同一组患者治疗前后的某项指标,或比较两种方法对同一批实验材料的效果。

检验原理: 设有nn对观测值(X1,Y1),(X2,Y2),,(Xn,Yn)(X_1, Y_1), (X_2, Y_2), \ldots, (X_n, Y_n)。令Di=XiYiD_i = X_i - Y_i。如果两个总体没有显著差异,那么差值DiD_i的中位数应为0,即P(Di>0)=P(Di<0)=0.5P(D_i > 0) = P(D_i < 0) = 0.5。因此,我们可以对差值DiD_i应用中位数是否为0的符号检验。

检验步骤

  1. 计算差值:对每对数据计算差值Di=XiYiD_i = X_i - Y_i
  2. 提出假设
    • H0H_0:两个总体中位数相等(即MD=0M_D = 0
    • H1H_1:两个总体中位数不相等(或MD>0M_D > 0, MD<0M_D < 0
  3. 计算统计量:计算B=i=1nI(Di>0)B = \sum_{i=1}^{n} I(D_i > 0),即正差值的个数。忽略差值为0的对(或采用其他处理方法)。
  4. 统计推断:在H0H_0下,BB(n,0.5)B \sim B(n’, 0.5),其中nn’为非零差值的对数。计算p值并决策。

实例分析:两种饲料的养猪效果比较

现有两种饲料XXYY,对14头猪进行配对实验,记录每头猪使用两种饲料后的增重(单位:kg)。我们想检验两种饲料的增重效果是否有显著差异。

import numpy as np
from scipy.stats import binomtest

# 设置随机种子保证结果可复现
np.random.seed(321)

# 模拟成对实验数据:饲料X和饲料Y对14头猪的增重效果
# 假设饲料X的效果略好于Y
base_weight = np.random.normal(50, 5, 14)  # 猪的基础增重潜力
# 饲料X的效果:基础值 + 正效应 + 随机误差
feed_x = base_weight + np.random.normal(1.0, 0.8, 14)
# 饲料Y的效果:基础值 + 随机误差
feed_y = base_weight + np.random.normal(0, 0.8, 14)

# 确保数据为正数
feed_x = np.abs(feed_x)
feed_y = np.abs(feed_y)

print("饲料X增重数据:", feed_x.round(2))
print("饲料Y增重数据:", feed_y.round(2))
print("\n配对差值 (X - Y):", (feed_x - feed_y).round(2))

# 进行符号检验
# 计算差值
diff = feed_x - feed_y
# 计算正差值的个数(即X > Y的对数)
k_pos = np.sum(diff > 0)
# 计算非零差值的总对数(忽略差值为0的情况,本例模拟数据中应无恰好为0的差值)
n_nonzero = np.sum(diff != 0)

print(f"\n非零差值的对数 n' = {n_nonzero}")
print(f"正差值的个数 k = {k_pos}")

# 首先,检验备择假设为 'greater'(X的中位数 > Y的中位数)
print("\n1. 检验 H0: M_X <= M_Y  vs H1: M_X > M_Y")
res_greater = binomtest(k_pos, n_nonzero, p=0.5, alternative='greater')
print(f"   p值: {res_greater.pvalue:.4f}")

# 然后,检验备择假设为 'less'(X的中位数 < Y的中位数)
print("\n2. 检验 H0: M_X >= M_Y  vs H1: M_X < M_Y")
res_less = binomtest(k_pos, n_nonzero, p=0.5, alternative='less')
print(f"   p值: {res_less.pvalue:.4f}")

# 综合判断
alpha = 0.05
print(f"\n综合判断 (显著性水平 alpha={alpha}):")
if res_greater.pvalue < alpha:
    print("   -> 拒绝'X效果不大于Y'的原假设,认为饲料X的增重效果显著优于Y。")
elif res_less.pvalue < alpha:
    print("   -> 拒绝'X效果不小于Y'的原假设,认为饲料X的增重效果显著劣于Y。")
else:
    print("   -> 两个单侧检验均不显著。")
    print("   -> 没有充分证据表明两种饲料的增重效果存在显著差异。")
    # 也可以直接做双侧检验
    res_two_sided = binomtest(k_pos, n_nonzero, p=0.5, alternative='two-sided')
    print(f"   双侧检验p值: {res_two_sided.pvalue:.4f},同样大于{alpha},支持无显著差异的结论。")

在这个模拟例子中,由于我们设定了XX的效果略好于YY,但加入了随机误差,结果可能出现两种情况:如果正差值个数足够多,可能会得出XX优于YY的结论;如果正负差值数量接近,则可能得出无显著差异的结论。这正体现了假设检验的思想:只有证据足够强时,我们才拒绝原假设。

📝 动手练一练

  1. 中位数检验:某工厂生产一种金属零件,设计长度为10.0厘米。质检员随机抽取了15个零件,测量长度如下(单位:厘米): 10.1, 9.8, 10.2, 9.9, 10.0, 10.3, 9.7, 10.1, 9.9, 10.0, 10.2, 9.8, 10.1, 9.9, 10.0 请使用符号检验,在0.05的显著性水平下,判断该生产线生产的零件长度中位数是否等于设计值10.0厘米。(备择假设:中位数不等于10.0厘米)

  2. 成对样本比较:为了研究一种新的教学方法的效果,选取了10名学生在传统方法(A)和新方法(B)教学后分别进行测试,成绩如下表:

    学生方法A方法B
    17882
    28587
    37275
    49088
    57680
    68892
    78185
    87983
    98484
    107779
    请使用符号检验,判断新教学方法(B)是否比传统方法(A)更能提高学生成绩(即检验差值B-A的中位数是否大于0)。

参考答案:

  1. 对于中位数检验问题,我们需要计算大于10.0的样本个数,然后进行双侧二项检验。

    import numpy as np
    from scipy.stats import binomtest
    
    lengths = np.array([10.1, 9.8, 10.2, 9.9, 10.0, 10.3, 9.7, 10.1, 9.9, 10.0, 10.2, 9.8, 10.1, 9.9, 10.0])
    M0 = 10.0
    k = np.sum(lengths > M0)  # 计算大于10.0的个数
    # 注意:恰好等于10.0的样本不提供方向信息,需要从有效样本中剔除
    n_valid = np.sum(lengths != M0)
    res = binomtest(k, n_valid, p=0.5, alternative='two-sided')
    print(f"大于{M0}的个数: {k}, 有效样本量: {n_valid}")
    print(f"双侧检验p值: {res.pvalue:.4f}")
    if res.pvalue < 0.05:
        print("结论:拒绝原假设,零件长度中位数不等于10.0厘米。")
    else:
        print("结论:没有充分证据拒绝原假设,不能认为中位数不等于10.0厘米。")

    运行结果提示:15个样本中有3个恰好等于10.0被剔除,剩余12个有效样本中大于10.0的有6个,双侧检验p值约为1.0,远大于0.05,因此不能拒绝原假设,即认为零件长度中位数与设计值10.0厘米无显著差异。

  2. 对于成对样本比较,我们计算每个学生B-A的差值,然后检验正差值的个数是否显著多于期望。

    import numpy as np
    from scipy.stats import binomtest
    
    score_A = np.array([78, 85, 72, 90, 76, 88, 81, 79, 84, 77])
    score_B = np.array([82, 87, 75, 88, 80, 92, 85, 83, 84, 79])
    diff = score_B - score_A
    print("差值 (B-A):", diff)
    k = np.sum(diff > 0)  # 正差值的个数
    # 注意:第9名学生差值为0,需要从有效样本中剔除
    n_valid = np.sum(diff != 0)
    print(f"正差值个数: {k}, 有效样本对数: {n_valid}")
    # 检验备择假设为 'greater' (B的中位数 > A的中位数)
    res = binomtest(k, n_valid, p=0.5, alternative='greater')
    print(f"单侧(大于)检验p值: {res.pvalue:.4f}")
    if res.pvalue < 0.05:
        print("结论:拒绝原假设,新教学方法(B)显著优于传统方法(A)。")
    else:
        print("结论:没有充分证据表明新教学方法(B)更优。")

    运行结果提示:观察差值,大部分为正,因此正差值个数k会比较大。进行单侧检验后,p值很可能小于0.05,从而得出新教学方法显著优于传统方法的结论。

本章小结

本节我们学习了非参数统计中的一种基础而重要的方法——符号检验

要点回顾

  1. 核心思想:符号检验通过将样本观测值与某个参考值(如假设的中位数)进行比较,仅利用比较结果的“符号”(正或负)进行统计推断。它对总体的分布形式不做任何要求,具有很好的稳健性。
  2. 检验问题:主要用于两类问题:(1) 检验单个总体的中位数是否等于某个指定值;(2) 基于成对样本,检验两个相关总体的中位数是否相等。
  3. 检验统计量:构造计数统计量BB(正号的个数),在原假设成立时,BB服从二项分布B(n,0.5)B(n, 0.5)。这是进行所有推断的理论基础。
  4. 实施步骤:无论是单样本还是成对样本,最终都转化为对二项分布参数p=0.5p = 0.5的检验。通过计算p值,并与显著性水平比较,做出拒绝或不拒绝原假设的决策。
  5. Python工具scipy.stats.binomtest函数为我们提供了便捷的符号检验实现,可以直接计算检验统计量、p值和置信区间。

行动清单: 学完本节,你可以立即动手做以下事情:

  1. 重估你的数据:回顾你手头的数据分析项目,是否存在需要比较中位数或处理非正态数据的场景?尝试用符号检验替代传统的t检验,看看结论是否一致或更具说服力。
  2. 代码实践:在Python环境中,使用scipy.stats.binomtest函数,对你熟悉的某个数据集(如某门课程的学生成绩)进行中位数检验,或对某个前后测实验数据进行成对比较。
  3. 思考局限性:符号检验只利用了数据的符号信息,而忽略了具体的数值大小。思考在什么情况下这种忽略是合理的(如数据存在严重异常值),在什么情况下可能会损失信息、导致检验效能降低?这为学习下一节更高效的Wilcoxon符号秩检验做好了铺垫。

— 小象教研组

配套学习资源与课件
  • 第7章课件:非参数统计(PDF · 1.8MB)
    下载
🎁 免费学习资源

领取《小象 11GB VIP 课件资料包与大厂真题手册》

包含全套实战 Jupyter 源码、清洗后数据集、大厂高频面试真题与专属学员答疑交流群。

  • 完整 Python / 数据分析 Jupyter 实战源码
  • 大厂真实业务数据集与练习题
  • 微信扫码添加课程顾问,免费获取网盘下载链接
微信二维码:扫码添加课程顾问微信扫码添加顾问