simple resueltos promedio ponderado movil exponencial ejercicios ejemplo moving-average

moving average - resueltos - ¿Cómo calcular el promedio móvil sin mantener el recuento y el total de datos?



promedio movil simple pdf (6)

Estoy tratando de encontrar una manera de calcular un promedio acumulado móvil sin almacenar el conteo y los datos totales que se reciben hasta ahora.

Se me ocurrieron dos algoritmos, pero ambos necesitan almacenar el conteo:

  • nuevo promedio = ((conteo antiguo * datos antiguos) + datos siguientes) / próximo conteo
  • nuevo promedio = promedio anterior + (próximo dato - promedio anterior) / próximo recuento

El problema con estos métodos es que el recuento se hace cada vez más grande y se pierde precisión en el promedio resultante.

El primer método usa el recuento antiguo y el próximo recuento, que obviamente están separados por 1. Esto me hizo pensar que tal vez haya una forma de eliminar el conteo, pero lamentablemente aún no lo he encontrado. Sin embargo, me ayudó un poco más, lo que resultó en el segundo método, pero el recuento todavía está presente.

¿Es posible, o solo estoy buscando lo imposible?


Aquí hay otra respuesta que ofrece comentarios sobre cómo Muis , Abdullah Al-Ageel y la respuesta de Flip son todos matemáticamente la misma cosa, excepto que están escritos de manera diferente.

Claro, tenemos el análisis de José Manuel Ramos explicando cómo los errores de redondeo afectan a cada uno de manera ligeramente diferente, pero eso depende de la implementación y cambiaría en función de cómo se aplica cada respuesta al código.

Sin embargo, hay una gran diferencia

Está en la N Muis , la k Flip y la n de Abdullah Al-Ageel . Abdullah Al-Ageel no explica exactamente qué debería ser n , pero N k difieren en que N es " el número de muestras donde desea promediar más ", mientras que k es el recuento de valores muestreados. (Aunque tengo dudas sobre si llamar a N es exacto el número de muestras ).

Y aquí llegamos a la respuesta a continuación. Es esencialmente el mismo viejo promedio móvil ponderado exponencial que los demás, por lo que si estaba buscando una alternativa, deténgase aquí mismo.

Media móvil ponderada exponencial

Inicialmente:

average = 0 counter = 0

Para cada valor:

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

La diferencia es la parte min(counter, FACTOR) . Esto es lo mismo que decir min(Flip''s k, Muis''s N) .

FACTOR es una constante que afecta la rapidez con que el promedio "alcanza" la última tendencia. Cuanto menor sea el número, más rápido. (En 1 ya no es un promedio y simplemente se convierte en el último valor).

Esta respuesta requiere el contador counter ejecución. Si es problemático, el min(counter, FACTOR) puede reemplazarse con solo FACTOR , convirtiéndolo en la respuesta de Muis . El problema al hacer esto es que la media móvil se ve afectada por cualquier average se inicie. Si se inicializó en 0 , ese cero puede tomar mucho tiempo para salir del promedio.

Cómo termina buscando


De un blog sobre la ejecución de cálculos de varianza muestral, donde la media también se calcula utilizando el método de Welford :

Lástima que no podamos subir imágenes SVG.


La respuesta de Flip es computacionalmente más consistente que la de Muis.

Utilizando el formato de número doble, puede ver el problema de redondeo en el enfoque de Muis:

Cuando divide y resta, aparece un redondeo en el valor almacenado anterior, cambiándolo.

Sin embargo, el enfoque Flip conserva el valor almacenado y reduce el número de divisiones, por lo tanto, reduce el redondeo y minimiza el error propagado al valor almacenado. Agregar solo mostrará redondeos si hay algo que agregar (cuando N es grande, no hay nada que agregar)

Esos cambios son notables cuando haces una media de grandes valores que tienden a cero.

Te muestro los resultados usando un programa de hoja de cálculo:

En primer lugar, los resultados obtenidos:

Las columnas A y B son los valores n y X_n, respectivamente.

La columna C es el enfoque Flip, y el D es el enfoque Muis, el resultado almacenado en el medio. La columna E se corresponde con el valor medio utilizado en el cálculo.

Un gráfico que muestra la media de los valores pares es el siguiente:

Como puede ver, hay grandes diferencias entre ambos enfoques.


Simplemente puede hacer:

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

Donde N es la cantidad de muestras en las que desea promediar. Tenga en cuenta que esta aproximación es equivalente a una media móvil exponencial. Ver: Calcular promedio móvil / móvil en C ++


Un ejemplo usando javascript, para comparar:

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


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

Esto supone que el conteo solo cambia en un valor. En caso de que se cambie por valores M, entonces:

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

Esta es la fórmula matemática (creo que la más eficiente), creen que pueden hacer más código por ustedes mismos