web-dev-qa-db-ja.com

C ++でローリング/移動平均を計算する

私はこれがブーストで達成できることを知っています:

boost :: accumulatorsを使用して、ローリングウィンドウサイズをリセットするにはどうすればよいですか、余分な履歴を保持しますか?

しかし、私は本当にブーストの使用を避けたいです。私はグーグルで検索しましたが、適切なまたは読みやすい例は見つかりませんでした。

基本的に、最新の1000個の数値をデータサンプルとして使用して、浮動小数点数のストリームの進行中のストリームの移動平均を追跡します。

これを達成する最も簡単な方法は何ですか?


円形配列、指数移動平均、より単純な移動平均を使用して実験したところ、円形配列の結果が私のニーズに最も適していることがわかりました。

38
goji

要素を前の要素に追加して保存する1000個の要素の円形配列が必要です。これは増加する合計になり、要素の任意の2つのペア間の合計を常に取得し、数で除算できます。それらの間の要素の、平均を得るために。

ニーズが単純な場合は、指数移動平均を使用してみてください。

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

簡単に言うと、アキュムレータ変数を作成し、コードが各サンプルを見ると、コードは新しい値でアキュムレータを更新します。 0と1の間の定数「アルファ」を選択し、これを計算します。

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

特定のサンプルの効果が約1000サンプルだけ続く「アルファ」の値を見つける必要があります。

うーん、ここに置いたので、これがあなたに適しているかどうかは実際にはわかりません。問題は、1000が指数移動平均のかなり長いウィンドウであることです。浮動小数点計算のアンダーフローなしで、最後の1000個の数値に平均を広げるアルファがあるかどうかはわかりません。しかし、30個ほどの数値のような、より小さな平均が必要な場合、これは非常に簡単で迅速な方法です。

77
steveha

入力ストリームに加重平均を適用することにより、ローリング平均を概算できます。

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

この方法では、1000個のバケットを維持する必要はありません。ただし、これは近似値であるため、値は実際のローリング平均と完全には一致しません。

編集:@stevehaの投稿に気付いた。これは、アルファが1/Nの指数移動平均に相当します(この場合、1000バケットをシミュレートするためにNを1000に設定していました)。

14
jxh

ローリング平均とローリング標準偏差を計算する単純なクラス:

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

};
4
Erik Aronesty

リングバッファ を実装できます。 1000個の要素の配列と、開始インデックスと終了インデックス、および合計サイズを格納するいくつかのフィールドを作成します。次に、リングバッファに最後の1000個の要素を保存し、必要に応じて平均を再計算します。

1
Tim

1つの方法は、バッファ配列に値を循環的に保存することです。この方法で平均を計算します。

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

すべてが最新の値が動的であるループで実行されます。

0

リストを使用した10個のアイテムの単純な移動平均:

#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
Pedro Soares

私はこれを、かなり正気でない更新レート(50キロサンプル/秒)を持つハードリアルタイムシステムで頻繁に使用します。その結果、通常、スカラーを事前計算します。

N個のサンプルの移動平均を計算するには:scalar1 = 1/N; scalar2 = 1-scalar1; //または(1-1/N)then:

平均= currentSample * scalar1 + Average * scalar2;

例:10要素のスライド平均

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

注:これは、上記のstevehaによる回答の実用的な実装にすぎません。具体的な例を理解する方が簡単な場合があります。

0
baumann