Python 国产皮尔逊&x27;s关联实现在向其传递两组相同的数据时返回0.999…2

Python 国产皮尔逊&x27;s关联实现在向其传递两组相同的数据时返回0.999…2,python,scipy,pearson,pearson-correlation,Python,Scipy,Pearson,Pearson Correlation,我受够了scipy和numpy,决定继续进行另一个实现,基于某处的SO答案 from statistics import pstdev, mean def pearson(x, y): sx = [] sy = [] mx = mean(x) my = mean(y) stdx = pstdev(x) stdy = pstdev(y) for i in x: sx.append((i - mx) / stdx)

我受够了scipy和numpy,决定继续进行另一个实现,基于某处的SO答案

from statistics import pstdev, mean

def pearson(x, y):
    sx = []
    sy = []

    mx = mean(x)
    my = mean(y)

    stdx = pstdev(x)
    stdy = pstdev(y)

    for i in x:
        sx.append((i - mx) / stdx)

    for j in y:
        sy.append((j - my) / stdy)

    return sum([i * j for i, j in zip(sx, sy)]) / len(x)
我向它传递了一些数字,看看它是否给出了与scipy.stats.pearsonr相同的东西,结果似乎很好。接近尾声的数字有所不同,但没有任何突破性的东西

直到我尝试向它传递与
x
y
相同的数据集。当我这样做时,我得到了返回的
0.999999999999 2
,而scipy和numpy都说它是
1.0

这个实现有什么问题吗?我使用的是总体stdev,而不是示例stdev,据我所知,numpy和scipy都使用它。我想知道为什么它没有像应该的那样返回
1.0
。这可能是python本身的浮点问题吗?我在Py 2和Py 3中试过,两个版本都得到了
0.999…

如果需要,我传入的数据集是:


[7, 1, 5, 1, 8, 5, 9, 8, 5, 10, 5, 8, 1, 8, 8, 8, 10, 4, 8, 9, 9, 6, 8, 7, 8, 5, 10, 5, 6, 8, 8, 7, 9, 4, 6, 10, 7, 10, 4, 5, 4, 7, 4, 8, 9, 10, 9, 8, 7, 8, 6, 8, 6, 6, 5, 7, 7, 7, 7, 3, 7, 8, 6, 8, 5, 7, 8, 7, 8, 6, 8, 6, 9, 6, 6, 6, 8, 9, 5, 7, 9, 2, 9, 6, 7, 6, 7, 7, 5, 5, 7, 7, 8, 6, 9, 1, 3, 6, 7, 9, 7, 7, 6, 9, 9, 4, 9, 9, 7, 9, 6, 2, 2, 8, 4, 7, 7, 6, 3, 7, 3, 5, 10, 9, 8, 10, 8, 7, 4, 7, 8, 9, 8, 4, 7, 9, 7, 7, 6, 8, 8, 9, 9, 7, 4, 4, 7, 3, 9, 3, 1, 8, 3, 9, 4, 8, 3, 9, 8, 8, 7, 9, 9, 8, 10, 8, 3, 10, 4, 7, 7, 10, 8, 7, 8, 7, 1, 8, 9, 5, 7, 5, 5, 3, 5, 7, 7, 7, 2, 4, 1, 6, 9, 9, 7, 7, 10, 9, 2, 9, 8, 2, 5, 1, 2, 5, 9, 1, 4, 8, 9, 6, 4, 4, 7, 3, 7, 9, 4, 3, 7, 8, 7, 6, 8, 8, 7]

您对浮点行为的期望过于乐观。根据经验,您不会对结果不完全是1.0感到惊讶。例如,请尝试以下小得多的输入:

[7, 1, 5]
在我的框中,您的函数返回1.0000000000000002。“接近”1.0,但不完全是1.0。一般来说,这是您所能期望的最好结果

要了解原因,请思考此“应”计算的内容:

在数学上(在无限精度下工作),它应该总是返回True。但是在浮点运算中(无论使用多少精度,只要精度有界),它不可能总是True。事实上,反例很容易找到;比如,刚才在我的框中:

>>> math.sqrt(2)**2
2.0000000000000004
问题是,在有限精度下,
sqrt()
必然是一个多对一函数。它将域
1..N
压缩到
1..sqrt(N)范围内
,并且在有限精度下,域的基数大于范围的基数。因此,域中必须存在映射到范围中相同值的不同
x
y
,因此没有精确的函数逆


您的函数比普通的
sqrt
更复杂,但同样的原理也在发挥作用。

是的,这与浮点行为有关。您可以尝试使用十进制模块

from decimal import *
data = [7, 1, 5, 1, 8, 5, 9, 8, 5, 10, 5, 8, 1, 8, 8, 8, 10, 4, 8]
data = [Decimal(x) for x in data]
print(pearson(data, data))

请注意,您还需要使用小数来计算平均值和标准偏差。您可以使用
x**Decimal('0.5')
而不是
sqrt(x)
使用Decimal的sqrt函数,正如Tim Peters在评论中解释的那样。

…为什么不直接使用
scipy.stats
?无论如何,您可以试试啊,我明白了。谢谢!
x**Decimal(0.5)
不是使用
decimal
模块的正确方法。@juanpa.arrivillaga噢,我没有意识到decimal模块有自己的sqrt函数。是否有原因
x**decimal(0.5)
不能工作?@LeopoldVonBuschLight不,我的观点是你想使用
decimal('0.5')
而不是
decimal(0.5)
@juanpa.arrivillaga噢,你说得对。谢谢!
十进制
,默认情况下,使用的精度比本机二进制浮点更高-但我的答案仍然适用:每个有限精度
sqrt
都必须是多对一函数。对于其他函数:
十进制(0.5)
十进制(“0.5”)相同
,因为0.5恰好可以表示为二进制浮点。您应该使用
sqrt()
而不是
**0.5
(无论拼写如何),因为
sqrt()
可以保证精确到正确的四舍五入结果;
**
不能。
from decimal import *
data = [7, 1, 5, 1, 8, 5, 9, 8, 5, 10, 5, 8, 1, 8, 8, 8, 10, 4, 8]
data = [Decimal(x) for x in data]
print(pearson(data, data))