我试图找到一种计算移动累积平均值的方法,而不存储到目前为止收到的计数和总数据。
我提出了两种算法,但都需要存储计数:
这些方法的问题在于计数越来越大,导致平均值失去精确度。
第一种方法使用旧计数和下一计数,显然是1。这让我想到也许有一种方法可以删除计数,但遗憾的是我还没有找到它。它确实让我更进一步,导致第二种方法,但仍然存在计数。
是否可能,或者我只是在寻找不可能的事情?
答案 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)
答案 3 :(得分:11)
这是另一个答案,提供关于 Muis , Abdullah Al-Ageel 和 Flip 的答案的评论所有数学上的所有答案同样的事情除了说明不同。
当然,我们有JoséManuelRamos 的分析,解释了舍入错误对每个错误的影响有多么不同,但这取决于实现,并会根据每个答案如何应用于代码而发生变化。
它位于 Muis 的N
, Flip 的k
和 Abdullah Al-Ageel 中s n
。 Abdullah Al-Ageel 并不能解释n
应该是什么,但N
和k
的不同之处在于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
,则该零可能需要很长时间才能超出平均值。
答案 4 :(得分:7)
Flip的答案在计算上比Muis的答案更加一致。
使用双数字格式,您可以看到Muis方法中的舍入问题:
当您进行分割和减法时,前一个存储值中会出现一个舍入值,并对其进行更改。
但是,Flip方法会保留存储的值并减少分割数,从而减少舍入,并最大限度地减少传播到存储值的误差。如果要添加某些东西,只添加将导致舍入(当N很大时,没有什么可添加的)
当你将大数值的意思倾向于零时,这些变化是显着的。
我使用电子表格程序向您展示结果:
A和B列分别是n和X_n值。
C列是Flip方法,D one是Muis方法,结果存储在平均值中。 E列对应于计算中使用的中值。
显示偶数值均值的图表是下一个:
正如您所看到的,两种方法之间存在很大差异。
答案 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();
您也有IntSummaryStatistics
,DoubleSummaryStatistics
...
答案 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)