如何计算移动平均数而不保持计数和数据总量?

我试图find一种方法来计算一个移动的累计平均值,而不存储到目前为止收到的数量和总数据。

我想出了两种algorithm,但都需要存储计数:

  • 新的平均值=((旧的计数旧数据)+下一个数据)/下一个计数
  • 新的平均数=旧的平均数+(下一个数据 – 旧的平均数)/下一个数

这些方法的问题是计数变得越来越大,导致平均值的精度下降。

第一种方法使用旧的计数和下一个计数,明显是相隔1。 这让我想,也许有一种方法来删除计数,但不幸的是,我还没有find它。 它确实让我进一步,虽然,导致第二种方法,但仍然计数是存在的。

这是可能的,还是我只是在寻找不可能的东西?

你可以简单地做:

double approxRollingAverage (double avg, double new_sample) { avg -= avg / N; avg += new_sample / N; return avg; } 

其中N是您想要平均的样本数量。

请参阅: 使用C ++计算滚动/移动平均值

 New average = old average * (n-1)/n + new value /n 

这是假devise数只改变了一个值。 如果它被M值改变,那么:

 new average = old average * (n-len(M))/n + (sum of values in M)/n). 

这是math公式(我相信是最有效的),相信你可以自己做更多的代码

从运行样本方差计算的博客中 ,也使用Wellford方法计算均值:

在这里输入图像说明

太糟糕了,我们无法上传SVG图像。

一个使用javascript的例子,用于比较:

https://jsfiddle.net/drzaus/Lxsa4rpz/

 function calcNormalAvg(list) { // sum(list) / len(list) return list.reduce(function(a, b) { return a + b; }) / list.length; } function calcRunningAvg(previousAverage, currentNumber, index) { // [ avg' * (n-1) + x ] / n return ( previousAverage * (index - 1) + currentNumber ) / index; } 
 (function(){ // populate base list var list = []; function getSeedNumber() { return Math.random()*100; } for(var i = 0; i < 50; i++) list.push( getSeedNumber() ); // our calculation functions, for comparison function calcNormalAvg(list) { // sum(list) / len(list) return list.reduce(function(a, b) { return a + b; }) / list.length; } function calcRunningAvg(previousAverage, currentNumber, index) { // [ avg' * (n-1) + x ] / n return ( previousAverage * (index - 1) + currentNumber ) / index; } function calcMovingAvg(accumulator, new_value, alpha) { return (alpha * new_value) + (1.0 - alpha) * accumulator; } // start our baseline var baseAvg = calcNormalAvg(list); var runningAvg = baseAvg, movingAvg = baseAvg; console.log('base avg: %d', baseAvg); var okay = true; // table of output, cleaner console view var results = []; // add 10 more numbers to the list and compare calculations for(var n = list.length, i = 0; i < 10; i++, n++) { var newNumber = getSeedNumber(); runningAvg = calcRunningAvg(runningAvg, newNumber, n+1); movingAvg = calcMovingAvg(movingAvg, newNumber, 1/(n+1)); list.push(newNumber); baseAvg = calcNormalAvg(list); // assert and inspect console.log('added [%d] to list at pos %d, running avg = %d vs. regular avg = %d (%s), vs. moving avg = %d (%s)' , newNumber, list.length, runningAvg, baseAvg, runningAvg == baseAvg, movingAvg, movingAvg == baseAvg ) results.push( {x: newNumber, n:list.length, regular: baseAvg, running: runningAvg, moving: movingAvg, eqRun: baseAvg == runningAvg, eqMov: baseAvg == movingAvg } ); if(runningAvg != baseAvg) console.warn('Fail!'); okay = okay && (runningAvg == baseAvg); } console.log('Everything matched for running avg? %s', okay); if(console.table) console.table(results); })(); 

Flip的答案在计算上比Muis更为一致。

使用双数字格式,您可以看到Muis方法中的舍入问题:

Muis方法

当您分割和减去时,先前存储的值中会出现一个舍入,并将其更改。

然而,Flip方法保留了存储的值并减less了分割数量,因此减less了舍入,并且最小化了传播到存储值的错误。 如果有添加的话,仅添加会带来四舍五入的效果(当N很大的时候,没有什么要添加的)

翻转的方法

这些变化是非常显着的,当你把平均值看成是零的时候。

我使用电子表格程序向您展示结果:

首先,得到的结果是: 结果

A和B列分别是n和X_n值。

C列是Flip方法,D方法是Muis方法,结果存储在平均值中。 E列对应于计算中使用的中等值。

显示偶数值的平均值的图表是下一个值:

图形

正如你所看到的,两种方法都有很大的不同。