肯德尔tau检验#

肯德尔tau是衡量两个排序之间对应关系的指标。

考虑来自 [1] 的以下数据,该数据研究了不健康人肝脏中游离脯氨酸(一种氨基酸)和总胶原蛋白(一种常在结缔组织中发现的蛋白质)之间的关系。

下面的 xy 数组记录了两种化合物的测量值。观测值是成对的:每个游离脯氨酸的测量值与同一索引处的总胶原蛋白测量值来自同一个肝脏。

import numpy as np
# total collagen (mg/g dry weight of liver)
x = np.array([7.1, 7.1, 7.2, 8.3, 9.4, 10.5, 11.4])
# free proline (μ mole/g dry weight of liver)
y = np.array([2.8, 2.9, 2.8, 2.6, 3.5, 4.6, 5.0])

这些数据在 [2] 中使用 Spearman 相关系数进行了分析,Spearman 相关系数是一种与 Kendall's tau 类似的统计量,因为它也对样本之间的序数相关性敏感。让我们使用 Kendall's tau 进行类似的分析。

from scipy import stats
res = stats.kendalltau(x, y)
res.statistic
0.5499999999999999

对于具有强正序数相关性的样本,此统计量的值往往较高(接近 1),对于具有强负序数相关性的样本,此统计量的值较低(接近 -1),而对于序数相关性较弱的样本,此统计量的值幅度较小(接近零)。

该检验是通过将观察到的统计量值与零分布进行比较来执行的:零分布是在总胶原蛋白和游离脯氨酸测量值独立的原假设下得出的统计量值分布。

对于此检验,对于没有联系的大样本,零分布近似为方差为 (2*(2*n + 5))/(9*n*(n - 1)) 的正态分布,其中 n = len(x)

import matplotlib.pyplot as plt
n = len(x)  # len(x) == len(y)
var = (2*(2*n + 5))/(9*n*(n - 1))
dist = stats.norm(scale=np.sqrt(var))
z_vals = np.linspace(-1.25, 1.25, 100)
pdf = dist.pdf(z_vals)
fig, ax = plt.subplots(figsize=(8, 5))

def plot(ax):  # we'll reuse this
    ax.plot(z_vals, pdf)
    ax.set_title("Kendall Tau Test Null Distribution")
    ax.set_xlabel("statistic")
    ax.set_ylabel("probability density")

plot(ax)
plt.show()
../../_images/169a997d8ea3f0233310715e4ec642e08c11abb38db19b787594687fafa1f257.png

比较由 p 值量化:零分布中与观察到的统计量值一样极端或更极端的值的比例。在统计量为正的双侧检验中,大于转换后的统计量的零分布元素和小于观察到的统计量的负数的零分布元素都被认为是“更极端”。

fig, ax = plt.subplots(figsize=(8, 5))
plot(ax)
pvalue = dist.cdf(-res.statistic) + dist.sf(res.statistic)
annotation = (f'p-value={pvalue:.4f}\n(shaded area)')
props = dict(facecolor='black', width=1, headwidth=5, headlength=8)
_ = ax.annotate(annotation, (0.65, 0.15), (0.8, 0.3), arrowprops=props)
i = z_vals >= res.statistic
ax.fill_between(z_vals[i], y1=0, y2=pdf[i], color='C0')
i = z_vals <= -res.statistic
ax.fill_between(z_vals[i], y1=0, y2=pdf[i], color='C0')
ax.set_xlim(-1.25, 1.25)
ax.set_ylim(0, 0.5)
plt.show()
../../_images/7e08ef660e800e8707c9a7e6fbc11a77fc22f2d004c3fc7bd7827313ddc1bfe7.png
res.pvalue
0.09108705741631495

请注意,曲线的阴影区域与 scipy.stats.kendalltau 返回的 p 值之间存在轻微差异。这是因为我们的数据有联系,并且我们忽略了 scipy.stats.kendalltau 执行的零分布方差的联系校正。对于没有联系的样本,我们的图的阴影区域和 scipy.stats.kendalltau 返回的 p 值将完全匹配。

如果 p 值“小” - 也就是说,如果从独立分布中采样产生如此极端统计量值的数据的概率很低 - 这可以作为反对原假设的证据,支持另一种假设:总胶原蛋白和游离脯氨酸的分布不是独立的。请注意

  • 反之则不然;也就是说,该检验不用于为原假设提供证据。

  • 将被认为是“小”的值的阈值是在分析数据之前应做出的选择 [3] ,并考虑到假阳性(错误地拒绝原假设)和假阴性(未能拒绝错误的原假设)的风险。

  • 小的 p 值不是效应的证据;相反,它们只能为“显著”效应提供证据,这意味着它们不太可能在原假设下发生。

对于没有中等大小的联系的样本,scipy.stats.kendalltau 可以精确计算 p 值。但是,在存在联系的情况下,scipy.stats.kendalltau 会采用渐近近似。尽管如此,我们可以使用排列检验来精确计算零分布:在总胶原蛋白和游离脯氨酸独立的原假设下,每个游离脯氨酸测量值都同样有可能与任何总胶原蛋白测量值一起被观察到。因此,我们可以通过计算 xy 之间元素每种可能的配对下的统计量来形成精确的零分布。

def statistic(x):  # explore all possible pairings by permuting `x`
    return stats.kendalltau(x, y).statistic  # ignore pvalue
ref = stats.permutation_test((x,), statistic,
                             permutation_type='pairings')
fig, ax = plt.subplots(figsize=(8, 5))
plot(ax)
bins = np.linspace(-1.25, 1.25, 25)
ax.hist(ref.null_distribution, bins=bins, density=True)
ax.legend(['asymptotic approximation\n(many observations)',
           'exact null distribution'])
plot(ax)
plt.show()
../../_images/e0edf0c720b0f9c254a83e441dbab220210503ce0bf9e42aee657be5b0bec7f4.png
ref.pvalue
0.12222222222222222

请注意,此处计算的精确 p 值与上面 scipy.stats.kendalltau 返回的近似值之间存在显着差异。对于有联系的小样本,请考虑执行排列检验以获得更准确的结果。

参考文献#