我想知道是否有算法计算未绑定数据集的平均值和标准差。
例如,我正在监测一个测量值,比如电流。我想得到所有历史数据的平均值。每当有新值出现时,更新均值和stdev?因为数据太大而无法存储,我希望它可以在不存储数据的情况下即时更新均值和stdev。即使数据被存储,标准方式(d1 + ... + dn)/ n也不起作用,总和将吹灭数据表示。
我通过求和(d1 / n + d2 / n + ... d3 / n),如果n为hugh,则误差太大并累积。此外,在这种情况下,n是未绑定的。
数据的数量肯定是未绑定的,无论何时,它都需要更新值。
有人知道是否有算法吗?
答案 0 :(得分:18)
[问题改变了吗?也许我只读了一开始。我已更新/编辑以提供更好的答复:]
我所知道的并没有完美的解决方案(在常数记忆中),但我可以提供各种方法。
首先,对于基本计算,您只需要所有值(sum_x
)的总和,它们的平方和(sum_x2
)和总计数(n
)之和。然后:
mean = sum_x / n
stdev = sqrt( sum_x2/n - mean^2 )
可以从流中更新所有这些值(sum_x
,sum_x2
,n
)。
问题(如你所说)是处理溢出和/或有限的精度。如果在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的滑动计算。
这是一个广泛的表格,使计算显而易见,易于理解。列是:
sum(i+1) = (sum (i)+x(i))/i+1
到目前为止没有争议。 (1,1,-1,-1)=> 4*1² = 4.
相反:(2,-2)=> 2² + -2² = 4+4 = 8
。第一列用于3个值,第二列用于5个值(用于计算)。
也许我们可以同意,这是计算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个值
插入公式:
ð² = 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 (链接到维基百科)。
维基百科还有两篇文章可以帮助您摆脱这个问题:
这可能是天真统计计算中最常见的问题之一。
请注意,当均值保持在0左右时,问题不会发生,远小于方差。
答案 4 :(得分:0)