非常庞大的数据集的平均值和标准差

时间:2012-04-28 15:51:59

标签: algorithm statistics mean numerics large-data

我想知道是否有算法计算未绑定数据集的平均值和标准差。

例如,我正在监测一个测量值,比如电流。我想得到所有历史数据的平均值。每当有新值出现时,更新均值和stdev?因为数据太大而无法存储,我希望它可以在不存储数据的情况下即时更新均值和stdev。

即使数据被存储,标准方式(d1 + ... + dn)/ n也不起作用,总和将吹灭数据表示。

我通过求和(d1 / n + d2 / n + ... d3 / n),如果n为hugh,则误差太大并累积。此外,在这种情况下,n是未绑定的。

数据的数量肯定是未绑定的,无论何时,它都需要更新值。

有人知道是否有算法吗?

5 个答案:

答案 0 :(得分:18)

[问题改变了吗?也许我只读了一开始。我已更新/编辑以提供更好的答复:]

我所知道的并没有完美的解决方案(在常数记忆中),但我可以提供各种方法。

首先,对于基本计算,您只需要所有值(sum_x)的总和,它们的平方和(sum_x2)和总计数(n)之和。然后:

mean = sum_x / n
stdev = sqrt( sum_x2/n - mean^2 )

可以从流中更新所有这些值(sum_xsum_x2n)。

问题(如你所说)是处理溢出和/或有限的精度。如果在sum_x2如此之大以至于内部表示不包含单个平方值的大小值时,您可以看到浮点数。

避免此问题的一种简单方法是使用精确算术,但这会越来越慢(并且还使用O(log(n))内存)。

另一种可以帮助的方法是规范化值 - 如果您知道值通常为X,那么您可以对x - X进行计算,这会使总和变小(显然您可以添加X对于意思!)。这有助于推迟精度丢失的点(并且可以/应该与此处的任何其他方法结合 - 例如,当分箱时,您可以使用前一个箱的平均值)。见this algorithm (knuth's method) for how to do this progressively

如果您不介意(小的常数因素)O(n)内存成本,您可以重新启动每个N值(例如百万 - 更智能的仍然是调整此值通过检测精度何时太低),存储先前的均值和stdev然后合并为最终结果(因此您的平均值是来自最近运行总计和旧分箱值的适当加权值)。

分箱方法可能会推广到多个级别(你开始对bin进行分箱)并减少到O(log(n))内存使用,但我还没有弄清楚细节

最后,一个更实际的解决方案可能是对1000个值进行初始处理,然后并行开始新的求和。你可以显示两者的加权平均值,然后在另外1000个值之后,丢弃旧的总和(在逐渐减轻它们的重量之后)并开始一个新的集合。因此,您总是有两组总和,并在它们之间显示加权平均值,这样可以提供反映过去1000个值的连续数据(仅限)。在某些情况下,这将是足够好的,我想(它不是一个确切的值,因为它只是“最近的”数据,但它是平滑和有代表性的,并使用固定数量的内存)。

ps,后来我发生的事情 - 真的,“永远”这样做“无论如何都没有多大意义,因为你将达到价值绝对由旧数据支配的程度。最好使用“运行平均值”,它可以降低旧值的权重。例如,见https://en.wikipedia.org/wiki/Moving_average - 但是,我不知道与stdev相同的常见内容。

答案 1 :(得分:4)

有趣的问题。

让我们首先讨论它的意思,只是因为它有点简单。

你对正在运行的总计的舍入误差是正确的。对于足够大的数据集,它会破坏你的准确性。您喜欢预先输出数据,先将小数据合计;但当然这在你的情况下是不可能的。但是,通过保持多个运行总计,您可以实现排序数据的大部分好处。

一个概念性的例子,C或C ++ - 风格:

const double max_small  =    0.001;
const double max_medium = 1000.0;

double total_small;
double total_medium;
double total_large;

while(true) {
    const double datum = get_datum(); // (Use here whatever function you use to get a datum.)
    if (!is_datum_valid()) break;
    if (abs(datum) <= max_small) total_small += datum;
    else if (abs(datum) <= max_medium) total_medium += datum;
    else total_large += datum;
}

double total = 0.0;
total += total_small;
total += total_medium;
total += total_large;

在实际代码中,您可能会保留三个以上的运行总计 - 当然,您还将继续运行数据平方的总计 - 但上面的示例传达了这个想法。你可以填写详细信息。

另外,根据@ andrewcooke的想法,你可以用这种方式扩展循环:

while(true) {
    const double datum = get_datum();
    if (!is_datum_valid()) break;
    if (abs(datum) <= max_small) {
        total_small += datum;
        if (abs(total_small) > max_medium) {
            total_large += total_small;
            total_small = 0.0;
        }
    }
    else if (abs(datum) <= max_medium) total_medium += datum;
    else total_large += datum;
}

同样,您可以填写详细信息。祝你好运。

附录:标准偏差的实际计算

在这里的各种评论主题中提出了一个很好的问题,即如何在不事先了解均值的情况下计算标准偏差。幸运的是,已知一种技巧来计算标准偏差。我准备了两页笔记来解释这个技巧here(用PDF格式)。

无论如何,在运行统计数据中包含标准偏差所需的全部内容不仅是数据,还包括数据的平方;当然,广场可以按照与数据本身相同的方式进行总计,遵循与上述代码相同的模式。

答案 2 :(得分:3)

<击>否。

(我想:不,但我被证明是错的)。

您可以携带总和和计数,以便

sum(i)=500, count(i)=50, => avg:=10
next value = 20
sum=520, count=51 => avg:= 10.19

<击> 但是stddev无法以这种方式构建。你必须为新均值产生每个值的增量,并将它们平方,然后才能除以N. 然而:问题是,那些是什么样的价值观(从数学的角度来看 - 远离物理学!:))。在正常情况下,我不希望在2000个元素之后改变值。否则,首先构建mean和stddev可能会有问题。

对于2000个元素,应该可以快速计算该值。

也许您可以使用缓冲区,并且每2000个值始终计算最后2000个值的avg和stddev。这是否是有意义的数据是你必须决定的。


我们无法在聊天中继续讨论,......

因为它还没有详细说明降价。所以我用我的帖子来阐明我的立场,主要是用thb来评论,但是安德鲁似乎也相信stddev的滑动计算。

这是一个广泛的表格,使计算显而易见,易于理解。列是:

  • i:运行索引。我们首先计算值1-3,然后计算值1-5。
  • x(i)是由我任意选择的数据。 3,4,5和4,6
  • 总结就是他们总结的结果。有趣的是该组的最后一个:12和22.注意:我们不会将3个值和2个值的总和用于前3个,然后是前5个。
  • 平均值仅为12/3或22/5。如果你知道我和总和,可以计算平均滑动。 sum(i+1) = (sum (i)+x(i))/i+1到目前为止没有争议。
  • 要计算stddev。,我们必须将每个值的差值取平均值,并将其平方(从而松开符号,否则会使差异无效 - 它将始终为0)。第二个影响是,很少的大距离导致比许多小距离更大的stddev。距离(1,1,-1,-1)=> 4*1² = 4.相反:(2,-2)=> 2² + -2² = 4+4 = 8。第一列用于3个值,第二列用于5个值(用于计算)。
  • 下一列(最后一个)²进行平方。
  • 总结
  • 除以n-1
  • 取平方根

spreadsheed with calculation (oocalc screenshot)

也许我们可以同意,这是计算stddev的有效方法。现在的问题是,如果你知道完整的第3行(除了x(3)= 5),如何计算它,现在你得到两个单独的值(4,6),如表中所示,但不知道(x( i)因为i = 1,2,3。

我的说法失败了:你可以。

好的 - 试过用你的配方。

ð²= 1 /(N-1)(Sum(x i ²) - 1 / N(Sum(x i ))²)

因此对于我得到的4个值

  • N = 5
  • sum(x i )= 22
  • sum(x i ²)= 102

插入公式:

ð² = 1/(N-1) (Sum (x<sub>i</sub>²) - 1/N (Sum (x<sub>i</sub>))²)
ð² = 1/4 (102 - 1/5 (22²))
ð² = 1/4 (102 - 1/5 (484))
ð² = 1/4 (102 - 96.8)
ð² = 1/4 (5.2)
ð² = 1.3
ð  = 1.140

我的结果是1.14,你的是1.14 所以有一个捷径。非常有趣 - 我仍然感到惊讶。

答案 3 :(得分:2)

事实上,即使在计算标准差时使用较小的数据集,也应该不计算平方和。这个问题被称为 catastrophic cancellation (链接到维基百科)。

维基百科还有两篇文章可以帮助您摆脱这个问题:

  • Kahan summation algorithm,当对大量非常小的值求和时(例如,当对所有x / n值进行求和时),它具有避免系统误差的结转
  • Algorithms for calculating variance,特别是“在线”版本应适合大型数据集。它将逐步更新每次观察的平均值,因此值仍保留在您的数据范围内!您可能需要使用更高阶版本的方差,因为第一个在线算法仍然计算平均值与平均值的偏差,因此对于大n,这可能会破坏您的值范围。高阶版本中的M2应包含平均偏差平均值,它与输出的比例相符。

这可能是天真统计计算中最常见的问题之一。

请注意,当均值保持在0左右时,问题不会发生,远小于方差。

答案 4 :(得分:0)

所以,我不确定这是否是以前使用过的算法,但是我还是会提供它。我首先想到用错误的平均值计算标准偏差,然后根据实际平均值进行校正。这是我写的

图片