当将两个相同的数据集传递给它时,自制的皮尔森相关实现返回0.999 ... 2

时间:2017-06-15 17:25:23

标签: 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)

    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给出相同的东西,它看起来很好。最后一个数字左右不同,但没有任何开创性的......

在我尝试将同一组数据传递给xy之前。当我这样做的时候,我得到了回归0.9999999999999992,当时scipy和numpy都说1.0

这个实现有问题吗?我使用了人口stdev而不是样本,并且据我所知,numpy和scipy都使用了它。我想知道为什么这不应该返回1.0。可能是python本身的浮动问题?我已经在Py 2和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]

2 个答案:

答案 0 :(得分:2)

您对浮点行为的期望过于乐观。根据经验,你不会感到有点惊讶,结果并不是1.0。例如,尝试使用这么小的输入:

[7, 1, 5]

在我的方框中,您的函数返回1.0000000000000002。 "接近" 1.0,但不完全是1.0。总的来说,这是你所能想到的最好的。

为了获得有关原因的好线索,请考虑这个"应该"计算:

math.sqrt(x)**2 == x

数学上(以无限精度工作),应始终返回True。但是在浮点(无论使用多少精度,只提供精度有限),它永远不可能是真的。事实上,反例非常容易找到;就像,刚才在我的盒子上:

>>> math.sqrt(2)**2
2.0000000000000004

问题是,具有有限精度sqrt()必然是多对一的功能。它将域1..N压缩到范围1..sqrt(N)中,并且在有限精度下,域的基数大于范围的基数。因此,域中必须存在不同的xy,它们映射到范围内的相同值,因此没有确切的函数反转。

你的功能比普通sqrt更复杂,但同样的原则正在发挥作用。

答案 1 :(得分:0)

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

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) 使用小数的sqrt函数,如Tim Peters在评论中所述。