simple resueltos promedio ponderado móviles moviles movil metodo medias exponencial estadistica ejercicios doble centrado c++ boost moving-average

c++ - resueltos - promedio movil ponderado



Calcular el promedio móvil/móvil en C++ (9)

Sé que esto se puede lograr con un impulso de acuerdo con:

Usando boost :: accumulators, ¿cómo puedo restablecer un tamaño de ventana móvil, mantiene un historial adicional?

Pero realmente me gustaría evitar el uso de boost. Busqué en Google y no encontré ningún ejemplo adecuado o legible.

Básicamente, quiero rastrear el promedio móvil de un flujo continuo de una secuencia de números flotantes usando los 1000 números más recientes como muestra de datos.

¿Cuál es la forma más fácil de lograr esto?

Experimenté con el uso de una matriz circular, una media móvil exponencial y una media móvil más simple y encontré que los resultados de la matriz circular se adaptaban mejor a mis necesidades.


Básicamente, quiero rastrear el promedio móvil de un flujo continuo de una secuencia de números flotantes usando los 1000 números más recientes como muestra de datos.

Tenga en cuenta que a continuación se actualiza el total_ como elementos agregados / reemplazados, evitando el costoso O (N) recorrido transversal para calcular la suma necesaria para el promedio a demanda.

template <typename T, typename Total, int N> class Moving_Average { public: Moving_Average() : num_samples_(0), total_(0) { } void operator()(T sample) { if (num_samples_ < N) { samples_[num_samples_++] = sample; total_ += sample; } else { T& oldest = samples_[num_samples_++ % N]; total_ += sample - oldest; oldest = sample; } } operator double() const { return total_ / std::min(num_samples_, N); } private: T samples_[N]; int num_samples_; Total total_; };

Total se hace un parámetro diferente de T para apoyar, por ejemplo, utilizando un long long cuando se totalizan 1000 s long , un int para char s, o un double to total float s.

Esto es un poco defectuoso en el sentido de que num_samples_ podría pasar INT_MAX . Si le importa, podría usar un unsigned long long , o usar un miembro de datos bool extra para registrar cuando el contenedor se llena por primera vez mientras hace un ciclo num_samples_ alrededor de la matriz (mejor renombrar algo inofensivo como " pos ").


Clase simple para calcular la media móvil y también la desviación estándar rodante:

#define _stdev(cnt, sum, ssq) sqrt((((double)(cnt))*ssq-pow((double)(sum),2)) / ((double)(cnt)*((double)(cnt)-1))) class moving_average { private: boost::circular_buffer<int> *q; double sum; double ssq; public: moving_average(int n) { sum=0; ssq=0; q = new boost::circular_buffer<int>(n); } ~moving_average() { delete q; } void push(double v) { if (q->size() == q->capacity()) { double t=q->front(); sum-=t; ssq-=t*t; q->pop_front(); } q->push_back(v); sum+=v; ssq+=v*v; } double size() { return q->size(); } double mean() { return sum/size(); } double stdev() { return _stdev(size(), sum, ssq); } };


Podrías implementar un buffer en anillo . Haga una matriz de 1000 elementos y algunos campos para almacenar los índices de inicio y final y el tamaño total. Luego solo almacene los últimos 1000 elementos en el buffer de anillo, y vuelva a calcular el promedio según sea necesario.


Puede aproximar un promedio móvil aplicando un promedio ponderado en su flujo de entrada.

template <unsigned N> double approxRollingAverage (double avg, double input) { avg -= avg/N; avg += input/N; return avg; }

De esta manera, no necesita mantener 1000 cubos. Sin embargo, es una aproximación, por lo que su valor no coincidirá exactamente con un promedio móvil verdadero.

Editar: acabo de notar la publicación de @ steveha. Esto es equivalente a la media móvil exponencial, con el alfa siendo 1 / N (estaba tomando N para ser 1000 en este caso para simular 1000 cubos).


Si sus necesidades son simples, puede intentar usar un promedio móvil exponencial.

http://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average

En pocas palabras, usted hace una variable de acumulador, y cuando su código mira cada muestra, el código actualiza el acumulador con el nuevo valor. Usted elige un "alfa" constante que está entre 0 y 1, y calcula esto:

accumulator = (alpha * new_value) + (1.0 - alpha) * accumulator

Solo necesita encontrar un valor de "alfa" donde el efecto de una muestra dada solo dura aproximadamente 1000 muestras.

Hmm, en realidad no estoy seguro de que esto sea adecuado para ti, ahora que lo he puesto aquí. El problema es que 1000 es una ventana bastante larga para un promedio móvil exponencial; No estoy seguro de que exista un alfa que disemine el promedio en los últimos 1000 números, sin subdesbordamiento en el cálculo del punto flotante. Pero si quería un promedio más pequeño, como 30 números más o menos, esta es una manera muy fácil y rápida de hacerlo.


Simplemente necesita una matriz circular de 1000 elementos, donde agrega el elemento al elemento anterior y lo almacena ... Se convierte en una suma creciente, donde siempre se puede obtener la suma entre dos pares de elementos y se divide por el número de elementos entre ellos, para ceder el promedio.


Una forma puede ser almacenar de forma circular los valores en la matriz de almacenamiento intermedio. y calcule el promedio de esta manera.

int j = (int) (counter % size); buffer[j] = mostrecentvalue; avg = (avg * size - buffer[j - 1 == -1 ? size - 1 : j - 1] + buffer[j]) / size; counter++; // buffer[j - 1 == -1 ? size - 1 : j - 1] is the oldest value stored

Todo se desarrolla en un ciclo donde el valor más reciente es dinámico.


Utilizo esto bastante a menudo en sistemas duros en tiempo real que tienen tasas de actualización bastante locas (50kilosamples / sec) Como resultado, normalmente precomputo los escalares.

Para calcular una media móvil de N muestras: escalar1 = 1 / N; scalar2 = 1 - escalar1; // o (1 - 1 / N) luego:

Average = currentSample * scalar1 + Average * scalar2;

Ejemplo: promedio deslizante de 10 elementos

double scalar1 = 1.0/10.0; // 0.1 double scalar2 = 1.0 - scalar1; // 0.9 bool first_sample = true; double average=0.0; while(someCondition) { double newSample = getSample(); if(first_sample) { // everybody forgets the initial condition *sigh* average = newSample; first_sample = false; } else { average = (sample*scalar1) + (average*scalar2); } }

Nota: esto es solo una implementación práctica de la respuesta dada por steveha arriba. A veces es más fácil entender un ejemplo concreto.


un promedio móvil simple para 10 elementos, usando una lista:

#include <list> std::list<float> listDeltaMA; float getDeltaMovingAverage(float delta) { listDeltaMA.push_back(delta); if (listDeltaMA.size() > 10) listDeltaMA.pop_front(); float sum = 0; for (std::list<float>::iterator p = listDeltaMA.begin(); p != listDeltaMA.end(); ++p) sum += (float)*p; return sum / listDeltaMA.size(); }