Oblicz średnią kroczącą / kroczącą w C++

Wiem, że jest to osiągalne z boostem jak na:

Używając boost:: accumulators, jak Mogę zresetować rozmiar toczącego się okna, czy zachowuje dodatkową historię?

Ale naprawdę chciałbym uniknąć używania boost. Wygooglowałem i nie znalazłem żadnych odpowiednich lub czytelnych przykładów.

Zasadniczo chcę śledzić średnią kroczącą bieżącego strumienia strumienia liczb zmiennoprzecinkowych przy użyciu najnowszych liczb 1000 jako próbki danych.

Jaki jest najprostszy sposób na osiągnąć to?


Eksperymentowałem z użyciem tablicy kołowej, wykładniczej średniej ruchomej i prostszej średniej ruchomej i odkryłem, że wyniki z tablicy kołowej najlepiej odpowiadały moim potrzebom.

Author: Community, 2012-06-12

9 answers

Po prostu potrzebujesz okrągłej tablicy 1000 elementów, w której dodajesz element do poprzedniego elementu i przechowujesz go... Staje się sumą rosnącą, gdzie zawsze można uzyskać sumę pomiędzy dowolnymi dwoma parami elementów i podzielić przez liczbę elementów między nimi, aby uzyskać średnią.

 18
Author: Karthik Kumar Viswanathan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-12 04:50:22

Jeśli Twoje potrzeby są proste, możesz po prostu spróbować użyć wykładniczej średniej ruchomej.

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

Mówiąc prościej, tworzysz zmienną akumulatora, a gdy twój kod patrzy na każdą próbkę, kod aktualizuje akumulator o nową wartość. Wybierz stałą "alfa", która jest między 0 a 1 i Oblicz to:

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

Wystarczy znaleźć wartość "alfa", gdzie efekt danej próbki wystarcza tylko na około 1000 próbek.

Hmm, nie jestem pewien, czy to jest odpowiednie dla ciebie, teraz, gdy go tu umieściłem. Problem polega na tym, że 1000 jest dość długim oknem dla wykładniczej średniej ruchomej; nie jestem pewien, czy istnieje alfa, która rozłoży średnią na ostatnie liczby 1000, bez niedociągnięcia w obliczeniach zmiennoprzecinkowych. Ale jeśli chcesz mniejszą średnią, jak 30 liczb lub tak, jest to bardzo łatwy i szybki sposób, aby to zrobić.
 76
Author: steveha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-12 06:32:56

Można przybliżać średnią kroczącą, stosując średnią ważoną na strumieniu wejściowym.

template <unsigned N>
double approxRollingAverage (double avg, double input) {
    avg -= avg/N;
    avg += input/N;
    return avg;
}
W ten sposób nie musisz utrzymywać 1000 wiadrów. Jest to jednak przybliżenie, więc jego wartość nie będzie dokładnie odpowiadać rzeczywistej średniej kroczącej.

Edit: właśnie zauważyłem post @ steveha. Jest to równoważne wykładniczej średniej ruchomej, z alfa jest 1 / N (brałem N być 1000 w tym przypadku symulować 1000 wiadra).

 14
Author: jxh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-12 06:00:32

Zasadniczo chcę śledzić średnią kroczącą bieżącego strumienia strumienia liczb zmiennoprzecinkowych przy użyciu najnowszych liczb 1000 jako próbki danych.

Zauważ, że poniższe aktualizacje total_ jako elementy dodane/zastąpione, unikając kosztownego O (N) trawersowania w celu obliczenia sumy - potrzebnej dla średniej - na żądanie.

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 jest wykonany inny parametr z T do obsługi np. za pomocą long long przy sumie 1000 long s, an int dla char s, Lub a double do sumy float s.

Jest to nieco wadliwe w tym, że num_samples_ może przejść obok INT_MAX - jeśli Ci zależy, możesz użyć unsigned long long, lub użyć dodatkowego elementu danych bool do rejestrowania, kiedy kontener jest po raz pierwszy wypełniony podczas rowerowania num_samples_ wokół tablicy (najlepiej zmienić nazwę na coś nieszkodliwego jak "pos").

 11
Author: Tony Delroy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-08 14:28:31

Klasa prosta do obliczenia średniej kroczącej, a także odchylenia standardowego toczenia:

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

};
 3
Author: Erik Aronesty,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-09 20:58:30

Można zaimplementować bufor pierścienia . Utwórz tablicę zawierającą 1000 elementów i kilka pól do przechowywania indeksów początku i końca oraz całkowitego rozmiaru. Następnie wystarczy zapisać ostatnie 1000 elementów w buforze pierścienia i ponownie obliczyć średnią w razie potrzeby.

 1
Author: Tim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-12 04:50:58

Prosta średnia ruchoma dla 10 pozycji, używając listy:

#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();
}
 0
Author: Pedro Soares,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-27 00:46:21

Jednym ze sposobów może być cykliczne przechowywanie wartości w tablicy buforów. i Oblicz średnią w ten sposób.

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

Całość przebiega w pętli, w której ostatnia wartość jest dynamiczna.

 0
Author: Nilesh Kumar Jha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-01 18:13:20

Używam tego dość często w twardych systemach czasu rzeczywistego, które mają dość szalone szybkości aktualizacji (50kilosamples/sec) w wyniku czego zazwyczaj wstępnie obliczam Skalary.

Aby obliczyć średnią ruchomą N próbek: skalar1 = 1 / N; skalar2 = 1-skalar1; / / lub (1-1 / N) wtedy:

Average = currentSample * skalar1 + Average * skalar2;

Przykład: ślizgowa średnia 10 elementów

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

Uwaga: Jest to tylko praktyczna implementacja odpowiedzi udzielonej przez steveha powyżej. Czasami łatwiej zrozumieć konkretny przykład.

 0
Author: baumann,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-25 01:09:10