为什么我无法获得小于 2.2e-16 的 p 值?

Why can't I get a p-value smaller than 2.2e-16?

提问人:arandomlypickedname 提问时间:8/7/2011 最后编辑:JayPeerachaiarandomlypickedname 更新时间:8/17/2023 访问量:34728

问:

我在 R 中的 t 检验和卡方中发现了这个问题,但我认为这个问题通常适用于其他测试。如果我这样做:

a <- 1:10
b <- 100:110
t.test(a,b) 

我得到:.我从评论中知道这是 的值 - 最小的浮点数,使得 ,但当然 R 可以表示比这小得多的数字。我还从 R 常见问题解答中知道 R 必须将浮点数四舍五入到 53 个二进制数字的精度:R 常见问题解答t = -64.6472, df = 18.998, p-value < 2.2e-162.2e-16.Machine$double.eps1 + x != 1

几个问题:(1) 我将其解读为 53 个二进制数字的精度是否正确,或者 R 中的值是否计算不准确?(2) 为什么在进行此类计算时,即使精度有所损失,也无法提供显示较小 p 值的方法?(3) 有没有办法显示更小的 p 值,即使我失去了一些精度?对于单个测试,2 个小数点后有效数字就可以了,对于我要去 Bonferroni 正确的值,我需要更多。当我说“失去一些精度”时,我认为< 53 个二进制数字,但是 (4) 我是否完全错了,任何 p 值都非常不准确?(5) R 只是诚实而其他统计包不是吗?< .Machine$double.eps< .Machine$double.eps

在我的领域中,非常小的 p 值是常态,例如:http://www.ncbi.nlm.nih.gov/pubmed/20154341http://www.plosgenetics.org/article/info%3Adoi%2F10.1371%2Fjournal.pgen.1002215,这就是为什么我想表示如此小的 p 值。

感谢您的帮助,对不起,这么曲折的问题。

Precision R-FAQ Bonferroni

评论

1赞 arandomlypickedname 8/14/2011
好吧,我不接受这个答案。我想知道为什么我的问题被否决了这么多,因为我在下面(或我搜索过的网上任何地方)没有看到明确的答案。我会重写,希望能得到一些澄清
2赞 Ben Bolker 8/14/2011
请看下面的回答。我认为你的问题很明智,eWizardII 是对的......
1赞 Gavin Simpson 8/16/2011
在与 Ben 讨论聊天后,我意识到我误解了你的 Q——就像几个人从外观上看一样。我现在倾向于认为最初接受的答案是正确的——或者可能是 Ben 解释推理时的答案。对噪音表示歉意。
1赞 Iterator 8/16/2011
+1 这是一个很好的问题,因为了解它的答案对我们很多人来说是有启发性的,关于一些统计应用和我们对 R. :) 的假设我学到了几件事。我希望其他人能解决他们重新考虑的任何反对票。

答:

9赞 eWizardII 8/7/2011 #1

尝试这样的事情,看看这是否能为您提供所需的准确性。我相信它与结果的打印有关,而不是实际存储的计算机值,而实际存储的计算机值应该具有必要的精度。t.test(a,b)$p.value

评论

8赞 Richie Cotton 8/7/2011
这是错误的。正如 DWin 在他的回答中提到的,由于浮点数的处理方式,是可以存储的大于零的最小数字(在大多数系统上)。它没有被更准确地存储。2.2e-16
6赞 Richie Cotton 8/7/2011
对不起,我的评论中的语言草率。 其实是最小的s.t.,大致翻译过来就是“你能分辨数字的准确程度”。您可以表示较小的数字(请参见),但精度较低。2.2e-16x1 + x != x.Machine$double.xmin
2赞 IRTFM 8/7/2011
这在理论上很可能是错误的另一个原因是,建议在基因测试(测试有意义的唯一可能领域)中最有可能计数的 t.test 中使用是将正常理论测试应用于错误类型的数据。4e13
3赞 Ben Bolker 8/15/2011
请看我的回答:我认为你是对的,尽管我的回答确实提供了一些额外的信息。我给了你一个+1来抵消负面评价。我们可以看看其他评论者是否仍然不同意......
4赞 Gavin Simpson 8/16/2011
我完全误解了 OP 的问题,鉴于 Ben 的回答和聊天讨论,我应该道歉并撤消我的反对票(因此进行了编辑,以便我可以解锁我的投票)。
13赞 IRTFM 8/7/2011 #2

两个问题:

1) 1e-16 和 1e-32 的 p 值之间在统计意义上可能存在什么差异?如果您真的可以证明它的合理性,那么使用记录的值是要走的路。

2)当你对R的数值精度感兴趣时,你为什么要使用维基百科?

R-FAQ说:“其他[表示非整数]数字必须四舍五入到(通常)53位二进制数字的精度。16 位数字大约是限制。这是在控制台上获得准确性限制的方法:

> .Machine$double.eps
[1] 2.220446e-16

当在 [0,1] 范围内解释时,该数字实际上为零

评论

4赞 arandomlypickedname 8/7/2011
1)在进行大量Bonferroni校正时,了解真实数字是有帮助的,而不仅仅是“它很小”。
2赞 IRTFM 8/7/2011
啊,一种支出意义策略。我怀疑 Bonferroni 是否准确,大小为 23-16 的多次修正次数适合 0.05。这将是类似于 4.166667e+13 的比较。
0赞 IRTFM 8/7/2011
我写了,但它被 SO 评论界面的自动更正方面破坏了。of size 2.220446e-16
3赞 arandomlypickedname 8/13/2011
您在那里假设 p 值 0.05 对于我的应用程序来说已经足够了,但对于许多应用程序来说,它不是,现在也不是。例如,在本文的摘要中给出了一个 bonferroni 校正后的 p 值 ~1e-10:plosgenetics.org/article/...
4赞 Ben Bolker 8/16/2011
最后一条评论并不完全正确。 (在我的机器上是 2.22e-308)是可以与零区分开来的最小数字; 是可以与 1 区分开来的最小数字.......Machine$double.xmin.Machine$double.eps1+x
11赞 Richie Cotton 8/7/2011 #3

您链接到的维基百科页面是 R 不使用的 Decimal64 类型——它使用标准问题的双精度值。

首先,帮助页面中的一些定义。.Machine

double.eps:最小的正浮点数“x”,使得 '1 + x != 1'。...通常为“2.220446e-16”。

double.xmin:最小的非零归一化浮点数 ...通常为“2.225074e-308”。

因此,您可以表示小于 2.2e-16 的数字,但它们的准确性会变暗,并且会导致计算问题。尝试一些数字接近最小可表示值的示例。

2e-350 - 1e-350
sqrt(1e-350)

您在评论中提到您想进行 bonferroni 更正。我建议您不要为此滚动自己的代码,而是使用。 使用这个。p.adjust(your_p_value, method = "bonferroni")pairwise.t.test

23赞 Ben Bolker 8/14/2011 #4

在这里交换答案和评论时,我对几件事感到困惑。

首先,当我尝试 OP 的原始示例时,我得到的 p 值没有这里讨论的那么小(几个不同的 2.13.x 版本和 R-devel):

a <- 1:10
b <- 10:20
t.test(a,b)
## data:  a and b 
## t = -6.862, df = 18.998, p-value = 1.513e-06

其次,当我使组之间的差异更大时,我确实得到了@eWizardII建议的结果:

a <- 1:10
b <- 110:120
(t1 <- t.test(a,b))
# data:  a and b 
# t = -79.0935, df = 18.998, p-value < 2.2e-16
#
> t1$p.value
[1] 2.138461e-25

打印输出 in 的行为由其调用(也由其他统计测试函数调用,如 OP 所指出的)驱动,而 又调用 ,它表示的 p 值小于其值 (默认为)为 。我很惊讶地发现自己不同意这些普遍精明的评论者......t.teststats:::print.htestchisq.testformat.pvaleps.Machine$double.eps< eps

最后,尽管担心一个非常小的p值的精确值似乎很愚蠢,但OP是正确的,这些值经常被用作生物信息学文献中证据强度的指标 - 例如,人们可以测试100,000个候选基因并查看结果p值的分布(搜索“火山图”以获取此类程序的一个例子)。

评论

0赞 arandomlypickedname 8/20/2011
哎呀,对不起,在我无意中混淆了各种 t 检验的结果。你是对的,<-1:10,b <-10:20 的 p 值正如你所说。我会解决我的问题。
5赞 user1277593 9/27/2012 #5

某些 R 包可解决此问题。最好的方法是通过包装pspearman。

source("http://www.bioconductor.org/biocLite.R")
biocLite("pspearman")
library("pspearman")
a=c(1:110,110)
b=1:111
out <- spearman.test(a, b, alternative = "greater", approximation="t-distribution")
out$p.value

[1] 3.819961e-294

2赞 Vince 12/11/2013 #6

最近有同样的问题。统计学家建议:

A <- cor.test(…)
p <- 2* pt(A$statistic,  df = A$parameter, lower.tail=FALSE)

评论

0赞 Ben Bolker 12/27/2021
这还不错,但如果您的统计数据恰好为负数,您应该使用。 也会起作用。abs(A$statistic)A$p.value
0赞 krassowski 8/17/2023 #7

这是一个流行的问题,但令人惊讶的是,没有提到使用对数表示作为解决方案的答案。

在一些研究领域,特别是生物信息学(尤其是基因组学,但在其他组学领域越来越多),精确的log10(p值)被用来比较证据与零证据。通过传递到适当的分位数分布函数,可以在 R 中获取常见测试的 p 值对数。log.p=TRUE

t检验

a = 1:10
b = 110:120

log10_t_test = function(...) {
    model = t.test(...)
    # note: you need to modify below if passing `alternative` arg
    log_e_p = log(2) + unname(pt(abs(model$statistic), df=model$parameter, lower.tail=FALSE, log.p=TRUE))
    model$log10_pvalue = log_e_p / log(10)
    model
}

model = log10_t_test(a, b)
model$log10_pvalue  # gives  -24.6699

您可以根据 log10(p) 的朴素计算进行评估:

t(sapply(seq(2, 7), function(order_of_magnitude) {
    n = 10 ** order_of_magnitude
    a = rnorm(n, mean=0)
    b = rnorm(n, mean=0.05)
    model = log10_t_test(a, b)
    c(
        proper_log10p=model$log10_pvalue,
        naive_log10p=log10(model$p.value)
    )
}))
proper_log10p naive_log10p
-0.2239816 -0.2239816
-1.4719561 -1.4719561
-0.7009232 -0.7009232
-30.7052283 -30.7052283
-250.8593000 -250.8593000
-2737.2759952 -Inf

相关

log10_cor_test = function(x, y, ..., method='pearson') {
    model = cor.test(x, y, ..., method=method)
    if (method == 'spearman') {
        r = model$estimate
        n = length(x)  # note: this assumes no missing values
        statistic = r * sqrt((n - 2) / (1 - r**2))
        df = n - 2
    } else {
        statistic = model$statistic
        df = model$parameter
    }
    log_e_p = log(2) + unname(pt(abs(statistic), df=df, lower.tail=FALSE, log.p=TRUE))
    model$log10_pvalue = log_e_p / log(10)
    model
}

皮尔森

方便的是,这也使用了 t 统计量,并且可以直接从结果中提取统计量和自由度参数。cor.test

比较:

t(sapply(seq(2, 7), function(order_of_magnitude) {
    n = 10 ** order_of_magnitude
    a = seq(1, n)
    b = a + rnorm(n) * 10**7 # add strong noise
    model = log10_cor_test(a, b)
    c(
        proper_log10p=model$log10_pvalue,
        naive_log10p=log10(model$p.value)
    )
}))
proper_log10p naive_log10p
-2.435782e+00 -2.4357824
-6.848772E-01 -0.6848772
-1.073320E-01 -0.1073320
-7.630908E-01 -0.7630908
-1.815829e+02 -181.5829491
-1.735492e+05 -Inf

斯皮尔曼

这需要更多的手动工作,因为我们需要手动计算自由度 () 和统计量。n-2

如果您对 t 分布近似感到满意,则可以使用 公式 计算检验统计量 formula,并重用相同的函数。r * sqrt((n - 2) / (1 - r**2))pt

比较:

t(sapply(seq(2, 7), function(order_of_magnitude) {
    n = 10 ** order_of_magnitude
    a = seq(1, n)
    b = a + rnorm(n) * 10**7 # add strong noise
    model = log10_cor_test(a, b, method='spearman')
    c(
        proper_log10p=model$log10_pvalue,
        naive_log10p=log10(model$p.value)
    )
}))
proper_log10p naive_log10p
-6.525561E-02 -0.06535951
-3.388555E-01 -0.33891807
-7.684660E-03 -0.00768466
-1.337620E-02 -0.01337620
-1.874304e+02 -187.43039010
-1.682590e+05 -Inf