追踪 SciPy 的 ttest_ind() 函数所做的假设

机器算法验证 统计学意义 t检验 Python
2022-03-20 10:53:39

我正在尝试编写自己的 Python 代码来计算一个和两个有尾独立 t 检验的 t 统计量和 p 值。我可以使用正态近似值,但目前我试图只使用 t 分布。我未能将 SciPy 统计库的结果与我的测试数据相匹配。我可以用一双新的眼睛来看看我是否只是在某个地方犯了一个愚蠢的错误。

请注意,这与其说是一个编码问题,不如说是一个“为什么这个计算不能产生正确的 t-stat?” 我给出完整的代码,但不要指望任何软件建议。只是帮助理解为什么这是不对的。

我的代码:

import numpy as np
import scipy.stats as st

def compute_t_stat(pop1,pop2):

    num1 = pop1.shape[0]; num2 = pop2.shape[0];

    # The formula for t-stat when population variances differ.
    t_stat = (np.mean(pop1) - np.mean(pop2))/np.sqrt( np.var(pop1)/num1 + np.var(pop2)/num2 )

    # ADDED: The Welch-Satterthwaite degrees of freedom.
    df = ((np.var(pop1)/num1 + np.var(pop2)/num2)**(2.0))/(   (np.var(pop1)/num1)**(2.0)/(num1-1) +  (np.var(pop2)/num2)**(2.0)/(num2-1) ) 

    # Am I computing this wrong?
    # It should just come from the CDF like this, right?
    # The extra parameter is the degrees of freedom.

    one_tailed_p_value = 1.0 - st.t.cdf(t_stat,df)
    two_tailed_p_value = 1.0 - ( st.t.cdf(np.abs(t_stat),df) - st.t.cdf(-np.abs(t_stat),df) )    


    # Computing with SciPy's built-ins
    # My results don't match theirs.
    t_ind, p_ind = st.ttest_ind(pop1, pop2)

    return t_stat, one_tailed_p_value, two_tailed_p_value, t_ind, p_ind

更新:

在阅读了更多关于 Welch t 检验的内容后,我看到我应该使用 Welch-Satterthwaite 公式来计算自由度。我更新了上面的代码以反映这一点。

有了新的自由度,我得到了更接近的结果。我的双边 p 值与 SciPy 版本相差约 0.008 ......但这仍然是一个太大的错误,所以我仍然必须做一些不正确的事情(或者 SciPy 分布函数非常糟糕,但很难相信它们只精确到小数点后 2 位)。

第二次更新:

在继续尝试的同时,我认为当自由度足够高(大约 > 30)时,SciPy 的版本可能会自动计算 t 分布的正态近似值。所以我改用正态分布重新运行我的代码,计算结果实际上比我使用 t 分布时更远离 SciPy。

1个回答

通过使用 SciPy 内置函数 source(),我可以看到函数 ttest_ind() 源代码的打印输出。根据源代码,内置 SciPy 正在执行 t 检验,假设两个样本的方差相等。它没有使用 Welch-Satterthwaite 自由度。

我只想指出,至关重要的是,这就是为什么您不应该只信任库函数的原因。在我的情况下,我实际上确实需要对不等方差的总体进行 t 检验,并且自由度调整对于我将运行的一些较小的数据集可能很重要。SciPy 假设方差相等,但没有说明这个假设。

正如我在一些评论中提到的那样,对于 30 到 400 之间的样本量,我的代码和 SciPy 之间的差异约为 0.008,然后对于更大的样本量会慢慢变为零。这是等方差 t 统计量分母中额外 (1/n1 + 1/n2) 项的影响。准确性方面,这非常重要,尤其是对于小样本量。它肯定向我证实了我需要编写自己的函数。(可能还有其他更好的 Python 库,但至少应该知道这一点。坦率地说,令人惊讶的是,这在 ttest_ind() 的 SciPy 文档中并没有出现在前面和中心位置)。