我试图找到一种计算移动累积平均值的方法,而不存储到目前为止收到的计数和总数据.
我想出了两个算法,但都需要存储计数:
这些方法的问题在于计数越来越大,导致平均值的精度下降.
第一种方法使用旧计数和下一计数,显然是1.这让我想到也许有一种方法可以删除计数,但遗憾的是我还没有找到它.它确实让我更进一步,导致第二种方法,但仍然计数存在.
是可能的,还是我只是在寻找不可能的事情?
Mui*_*uis 86
你可以简单地做:
double approxRollingAverage (double avg, double new_sample) {
avg -= avg / N;
avg += new_sample / N;
return avg;
}
Run Code Online (Sandbox Code Playgroud)
哪里N是要平均值的样本数量.请注意,此近似值等于指数移动平均值.请参阅:使用C++计算滚动/移动平均值
小智 72
New average = old average * (n-1)/n + new value /n
Run Code Online (Sandbox Code Playgroud)
这假设计数仅改变一个值.如果它被M值改变,那么:
new average = old average * (n-len(M))/n + (sum of values in M)/n).
Run Code Online (Sandbox Code Playgroud)
这是数学公式(我相信最有效的公式),相信你可以自己做更多的代码
Fli*_*lip 26
从博客上运行样本方差计算,其中均值也使用Welford的方法计算:
太糟糕了,我们无法上传SVG图像.
ant*_*tak 13
这里有一个关于如何另一个答案提供评论的MUI,阿卜杜拉Ageel和翻转的回答是所有数学上同样的事情,除了有不同的写法.
当然,我们有JoséManuelRamos的分析,解释了舍入错误对每个错误的影响有多么不同,但这依赖于实现,并且会根据每个答案如何应用于代码而改变.
这是在Muis的N,Flip的k和Abdullah Al-Ageel的n. 阿卜杜拉Ageel并不完全解释什么n是应该的,但N和k在不同的N是" 在要平均值的样本数 ",而k采样值的计数.(虽然我怀疑是否准确调用N 样本数量.)
在这里,我们来到下面的答案.它基本上与其他指数加权移动平均值相同,因此如果您正在寻找替代方案,请立即停在此处.
原来:
average = 0
counter = 0
Run Code Online (Sandbox Code Playgroud)
对于每个值:
counter += 1
average = average + (value - average) / min(counter, FACTOR)
Run Code Online (Sandbox Code Playgroud)
不同之处在于min(counter, FACTOR)部分.这跟说的一样min(Flip's k, Muis's N).
FACTOR是一个常数,它影响平均"赶上"到最新趋势的速度.数字越小越快.(1它不再是平均值,只是变成最新值.)
这个答案需要运行计数器counter.如果有问题,min(counter, FACTOR)可以用Just替换FACTOR,把它变成Muis的答案.这样做的问题是移动平均线受average初始化的影响.如果它被初始化为0,那么零可能需要很长时间才能超出平均值.
Flip的答案在计算上比Muis更加一致.
使用双数字格式,您可以看到Muis方法中的舍入问题:
除法和减法时,前一个存储值中会出现一个舍入值,并对其进行更改.
但是,Flip方法会保留存储的值并减少分割数量,从而减少舍入,并最大限度地减少传播到存储值的误差.如果有要添加的东西,只添加将导致舍入(当N很大时,没有什么可添加的)
当你将大值的意思倾向于它们的意思为零时,这些变化是显着的.
我使用电子表格程序向您展示结果:
A列和B列分别是n和X_n值.
C列是Flip方法,D one是Muis方法,结果存储在平均值中.E列对应于计算中使用的中值.
显示偶数值均值的图表是下一个:
如您所见,两种方法之间存在很大差异.
使用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;
}
Run Code Online (Sandbox Code Playgroud)
(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);
})();Run Code Online (Sandbox Code Playgroud)
基于上述答案的简洁 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)
Run Code Online (Sandbox Code Playgroud)
用法:
x = RunningAverage()
x(0)
x(2)
x(4)
print(x)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
111787 次 |
| 最近记录: |