AssertionError: negative sum of square deviations

AssertionError: negative sum of square deviations

作为一个更大项目的一部分,我正在编写一个函数,它接受一个整数字典和 returns 一个字典,每个 "outer" 键都链接到均值和该子词典的标准偏差(即 (mean(dict[key1]), stdev(dict[key1])) )。我正在处理一个大型数据集(源文件是一个 2.8 GB 的 csv 文件),并且在计算其中一个子字典的标准差时出现断言错误。

虽然我将(并且目前正在)追踪导致以下错误的子字典,但我很好奇一般情况下会导致它,因此如果它发生在我的数据集中,我可以尽量避免它。

我收到的错误消息是:

AssertionError: negative sum of square deviations: -3734262324235.697754

来自代码:

import statistics as stat

try: #Check for single value error
    std = stat.stdev(val)
except stat.StatisticsError:
    std = 0

statiscs.py 中的代码是纯代码 Python - 在处理内部“平方和”时,您似乎是分数 class 中奇怪溢出错误的受害者statistics._ss 函数。

我认为你现在能做的最好的事情就是在 statistics.py 文件本身中使用 "if" 调用 pdb.set_trace 来设置 _ss 函数以交互方式查找导致错误的数据(代码中有注释,这部分会出现舍入错误)。它计算一个应该为零的分数 - 但对于舍入误差,并对该分数进行平方。但是在平方时,已经很大的分母本身被平方 - 这可能会触发 Python 的分数内部的错误,并在它应该接近零时返回一个非常大的值。

这样的 "if" 子句可以让您 (1) 绕过错误条件并 运行 您的代码到最后,在发现错误时将值强制为零; (2) 记下导致错误的值,并将其作为错误报告给 Python 语言本身。

@jsbueno 提到,这是 statistics.py 文件的问题。我也有同样的错误并通过将 statistics.stdev 替换为 numpy.std 而不是更改源代码来解决它。

我在非常小的数字上遇到了同样的问题。 sum(x²) 的准确计算得出的结果为零 (Fraction(0,1)),但 sum(x) 的准确计算给出了一个非常小的正分数,代表舍入误差和减法中的精度损失来自数据的意思。

statistics.py中的代码表明total2应该为零,但实际上它可以是任何小数,正数或负数。 total2的平方总是一个小的正分数

def _ss(data, c=None):
    """Return sum of square deviations of sequence data.

    If ``c`` is None, the mean is calculated in one pass, and the deviations
    from the mean are calculated in a second pass. Otherwise, deviations are
    calculated from ``c`` as given. Use the second case with care, as it can
    lead to garbage results.
    """
    if c is None:
        c = mean(data)
    T, total, count = _sum((x-c)**2 for x in data)
    # The following sum should mathematically equal zero, but due to rounding
    # error may not.
    U, total2, count2 = _sum((x-c) for x in data)
    assert T == U and count == count2
    total -=  total2**2/len(data)
    assert not total < 0, 'negative sum of square deviations: %f' % total
    return (T, total)

因此,方差总和可能在断言失败之前变为负值。

根本原因是在第一个 _sum 函数调用中对每个值求平方时发生的准确性损失。 float 或 np.float64 值通过浮点运算在列表理解中进行平方。

一种可能的更正方法是在对它求平方之前将 total2 转换为类型 T。它改变了语义,因为 _ss returns 类型为 T 的值而不是精确分数。另一种更准确的方法是在第一次调用 _sum 之前将 x-c 一劳永逸地转换为分数。在这两种情况下,计算也会 运行 更快。

最合适的更正并非微不足道,因为 _sum 还聚合了对 _coerce 的连续调用的类型。之前将数据转换为分数也会将结果类型更改为分数。