尝试规范化数据时出现巨大的数值错误

huge numerical errors when trying to normalize data

我经常通过程序在数据中处理一些数据。为了简单起见,让我们考虑数据是一系列相同大小的数字。 当数字高得不合理时,对数据进行标准化可能会有用。一种常见的转换是从所有值中减去平均值。在此转换之后,转换后的数据的平均值将为零。

在平均值为零后可以进行的其他常见转换是将数据除以标准差。应用此转换后,新数据具有单位方差。

当处理以这种方式标准化的数据时,我希望数值误差应该更小。但是我似乎无法进行这些转换,因为即使在我尝试计算标准偏差时也会出现数值错误。

Bellow 是 c# 中的示例代码,我在其中尝试计算标准偏差。即使没有(公式的)统计知识,也可以很容易地看出程序的输出应该为零。 (如果数据是常量数组,则数据平方的平均值等于平均值​​的平方。)

static double standardDeviation(double[] data)
{
    double sum = 0;
    double sumOfSquares = 0;
    foreach (double number in data)
    {
        sum += number;
        sumOfSquares += number * number;
    }
    double average = sum / data.Length;
    double averageOfSquares = sumOfSquares / data.Length;
    return Math.Sqrt(averageOfSquares - average * average);
}
static void Main(string[] args)
{
    double bigNumber = 1478340000000;
    double[] data = Enumerable.Repeat(bigNumber, 83283).ToArray();
    Console.WriteLine(standardDeviation(data));
}

由于数值错误,程序输出了一个巨大的数字而不是零:2133383.0308878

请注意,如果我省略 Math.Sqrt(即我将计算方差而不是标准差),那么误差会更高。

这是什么原因,我该如何写出更小的数字错误?

我认为您混淆了可能的最大/最小值(±5.0 × 10−324 到 ±1.7 × 10308)与 有效数字 可用的数量(15 - 16 ) 双倍。

在你的情况下,我会说你是在浪费数字,因为你没有先缩放输入,即将你的值转换为 1.47834,比例因子为 1 / 10^7 用于您的数值计算。

虽然您用于方差的公式在数学上是正确的——即如果您有无限精度——它可能会导致有限精度的麻烦。

对于N个数据X更好的方法是计算

variance = Sum{ square( X[i] - mean) }/ N

哪里

mean = Sum{ X[i] } /N

正如所写,这需要两次遍历数据。如果这很尴尬,您实际上可以一次性完成。您需要保留三个变量,n(到目前为止看到的数据项数)均值和方差。这些都应该初始化为 0(又名 0.0)。然后当你得到下一个数据项时 x:

n = n + 1
f = 1.0/n
d = x-mean
mean = mean + f*d
variance = (1.0-f)*(variance + f*d*d)

在处理一个数据项n后的每个阶段,均值、方差确实是目前为止数据的个数、均值和方差。