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

时间:2012-09-28 08:46:10

标签: moving-average

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

我提出了两种算法,但都需要存储计数:

  • 新平均值=((旧计数*旧数据)+下一数据)/下次计数
  • 新平均值=旧平均值+(下一个数据 - 旧平均值)​​/下一个计数

这些方法的问题在于计数越来越大,导致平均值失去精确度。

第一种方法使用旧计数和下一计数,显然是1。这让我想到也许有一种方法可以删除计数,但遗憾的是我还没有找到它。它确实让我更进一步,导致第二种方法,但仍然存在计数。

是否可能,或者我只是在寻找不可能的事情?

8 个答案:

答案 0 :(得分:84)

您可以这样做:

double approxRollingAverage (double avg, double new_sample) {

    avg -= avg / N;
    avg += new_sample / N;

    return avg;
}

其中N是您想要平均的样本数。 请注意,此近似值等于指数移动平均值。 请参阅:Calculate rolling / moving average in C++

答案 1 :(得分:66)

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

这假设计数仅改变了一个值。如果它被M值改变,那么:

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

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

答案 2 :(得分:23)

a blog开始运行样本方差计算,其中均值也使用Welford's method计算:

enter image description here

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

答案 3 :(得分:11)

这是另一个答案,提供关于 Muis Abdullah Al-Ageel Flip 的答案的评论所有数学上的所有答案同样的事情除了说明不同。

当然,我们有JoséManuelRamos 的分析,解释了舍入错误对每个错误的影响有多么不同,但这取决于实现,并会根据每个答案如何应用于代码而发生变化。

但是存在相当大的差异

它位于 Muis N Flip k Abdullah Al-Ageel 中s n Abdullah Al-Ageel 并不能解释n应该是什么,但Nk的不同之处在于N是“要平均的样本数k是采样值的计数。 (虽然我怀疑是否要调用N 样本数是准确的。)

在这里,我们来到下面的答案。它与其他人基本上是相同的旧 指数加权移动平均值 ,所以如果您正在寻找替代方案,请立即停止。

指数加权移动平均值

最初:

average = 0
counter = 0

对于每个值:

counter += 1
average = average + (value - average) / min(counter, FACTOR)

区别在于min(counter, FACTOR)部分。这与说min(Flip's k, Muis's N)相同。

FACTOR是一个常数,它会影响平均“赶上”最新趋势的速度。数字越小越快。 (在1,它不再是平均值,只是成为最新值。)

此答案需要运行计数器counter。如果有问题,可以将min(counter, FACTOR)替换为FACTOR,将其转换为 Muis 的答案。这样做的问题是移动平均线受初始化average的影响。如果它被初始化为0,则该零可能需要很长时间才能超出平均值。

它最终如何看待

Exponential moving average

答案 4 :(得分:7)

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

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

The Muis approach

当您进行分割和减法时,前一个存储值中会出现一个舍入值,并对其进行更改。

但是,Flip方法会保留存储的值并减少分割数,从而减少舍入,并最大限度地减少传播到存储值的误差。如果要添加某些东西,只添加将导致舍入(当N很大时,没有什么可添加的)

The Flip approach

当你将大数值的意思倾向于零时,这些变化是显着的。

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

首先,获得的结果为:Results

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

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

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

Graph

正如您所看到的,两种方法之间存在很大差异。

答案 5 :(得分:4)

使用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);
})();

答案 6 :(得分:0)

在Java8中:

LongSummaryStatistics movingAverage = new LongSummaryStatistics();
movingAverage.accept(new data);
...
average = movingAverage.getAverage();

您也有IntSummaryStatisticsDoubleSummaryStatistics ...

答案 7 :(得分:0)

基于上述答案的整洁的Python解决方案:

class RunningAverage():
    def __init__(self):
        self.average = 0
        self.n = 0
        
    def __call__(self, new_value):
        self.n += 1
        self.average = (self.average * (self.n-1) + new_value) / self.n 
        
    def __float__(self):
        return self.average
    
    def __repr__(self):
        return "average: " + str(self.average)

用法:

x = RunningAverage()
x(0)
x(2)
x(4)
print(x)