カウントとデータ合計を維持せずに移動平均を計算する方法は?


118

これまでに受信したカウントと合計データを保存せずに移動累積平均を計算する方法を探しています。

2つのアルゴリズムを考え出しましたが、どちらもカウントを保存する必要があります。

  • 新しい平均=((古いカウント*古いデータ)+次のデータ)/次のカウント
  • 新しい平均=古い平均+(次のデータ-古い平均)/次のカウント

これらのメソッドの問題は、カウントがますます大きくなり、結果として得られる平均の精度が失われることです。

最初の方法では、古いカウントと次のカウントを使用します。これらは明らかに1つ離れています。これにより、数を削除する方法があるのではないかと思いましたが、残念ながらまだ見つかりません。それでも私は少し先に行きました、その結果、2番目の方法になりましたが、まだカウントは存在しています。

それは可能ですか、それとも私は不可能を探しているだけですか?


1
数値的には、現在の合計と現在の数を保存するのが最も安定した方法です。それ以外の場合は、カウントが高くなると、next /(次のカウント)がアンダーフローし始めます。したがって、精度の低下が本当に心配な場合は、合計を保持してください。
AlexR 2016

1
参照してくださいウィキペディアen.wikipedia.org/wiki/Moving_average
xmedeko

回答:


91

あなたは単に行うことができます:

double approxRollingAverage (double avg, double new_sample) {

    avg -= avg / N;
    avg += new_sample / N;

    return avg;
}

どこNで平均したいサンプルの数です。この近似は指数移動平均に相当することに注意してください。参照:C ++でのローリング/移動平均の計算


3
この行の前に、Nに1を追加する必要はありませんか?平均+ = new_sample / N;
Damian

20
これは完全に正しいわけではありません。@Muisが説明するのは、指数的に重み付けされた移動平均です。これは、適切な場合もありますが、OPが要求したものとは正確には一致しません。例として、ほとんどのポイントが2から4の範囲にあるが、1つの値が100万以上の場合に予想される動作を考えます。EWMA(ここ)は、かなりの時間、その数百万の痕跡を保持します。OPで示される有限の畳み込みは、Nステップの直後にそれを失います。これには、一定のストレージという利点があります。
jma

9
それは移動平均ではありません。あなたが説明するのは、信号のジャンプに対する指数応答を作成する単極フィルターです。移動平均は、長さNを有する線形応答を作成
ruhigブラウナーを

3
これは平均の一般的な定義とはかなりかけ離れていることに注意してください。N = 5に設定して5つの5サンプルを入力すると、平均は0.67になります。
Dan Dascalescu

2
@DanDascalescu実際にはローリング平均ではないことは正しいですが、指定された値は1桁ずれています。にavg初期化する03.36、5 54.46後、10 秒後に終了します。cpp.sh / 2ryql長い平均の場合、これは確かに有用な近似です。
cincodenada

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

これは、カウントが1つの値だけ変更されたと想定しています。M値によって変更された場合:

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

これは数式です(私は最も効率的なものです)、自分でさらにコードを実行できると信じています


新しい価値の合計とは何ですか?それはあなたの元の公式の「新しい価値」とどういうわけか違うのですか?
2016年

2番目の例の@Mikhailでは、m新しい平均に新しい値が考慮されています。sum of new valueここではm、新しい平均を計算するために使用される新しい値の合計であると考えています。
Patrick Goley 2017年

9
最初の方が少し効率的です:new_average = (old_average * (n-1) + new_value) / n-除算の1つを削除します。
Pixelstix 2017

6,0,0,9の3つの要素の移動平均はどうですか?
Roshan Mehta 2017

1
この方程式を実装すると、値または移動平均は常にゆっくりと増加します。それは決して下がらない-上がるだけです。
anon58192932 2018年

30

平均分散もウェルフォードの方法を使用して計算されるサンプル分散計算の実行に関するブログから:

ここに画像の説明を入力してください

SVG画像をアップロードできません。


3
これは、除算が一般的な要素として使用されることを除いて、Muisが実装したものと似ています。したがって、1つの区分のみです。
フリップ

MuisはNの増加を考慮しないため、実際には@ Abdullah-Al-Ageel(本質的に可換数学)に近い。式のコピーと貼り付けのリファレンス:[nでの平均] = [n-1での平均] +(x-[n-1での平均])/ n
drzaus

2
@Flip&drwaus:MuisとAbdullah Al-Ageelのソリューションはまったく同じではありませんか?それは同じ計算ですが、異なる方法で書かれています。私にとってこれらの3つの答えは同一であり、これはより視覚的です(SOでMathJaxを使用できないのは残念です)。
user276648 2016年

21

MuisAbdullah Al-AgeelおよびFlipの答えが数学的に同じである方法についての解説を提供するさらに別の答えがあります書き方が異なるを除いて。

確かに、私たちはJoséManuel Ramosの分析で丸め誤差がそれぞれにわずかに異なる影響を与えることを説明していますが、それは実装に依存し、各回答がコードにどのように適用されたかに基づいて変化します。

しかし、かなり大きな違いがあります

それは中だMUISさんNフリップさんk、そしてアブドラアル・Ageelさんnアブドラ・アル・Ageelはかなり何を説明していませんnする必要がありますが、Nそしてkその中で異なるN「であるあなたは平均以上にしたいサンプル数ながら、」kサンプリング値の数です。(サンプルの数を呼び出すかどうかには疑問がN ありますがが正確が。)

そして、ここで私たちは以下の答えに行きます。それは基本的に他のものと同じ古い指数加重移動平均なので、別の方法を探しているならここで止めてください。

指数加重移動平均

最初は:

average = 0
counter = 0

各値について:

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

違いはmin(counter, FACTOR)部分です。これは言うことと同じmin(Flip's k, Muis's N)です。

FACTOR平均が最新のトレンドに「追いつく」速さに影響する定数です。数値が小さいほど速くなります。(1それはもはや平均ではなく、最新の値になるだけです。)

この回答には、ランニングカウンターが必要counterです。問題がある場合は、min(counter, FACTOR)をjust FACTORに置き換えて、Muisの答えに変えることができます。これを行う際の問題は、移動平均averageが初期化されたものの影響を受けることです。それがに初期化された場合0、そのゼロが平均から外れるまでに時間がかかる可能性があります。

最終的にどのように見えるか

指数移動平均


3
よく説明しました。OPが求めているので、私はあなたのグラフの単純な平均を見逃しています。
xmedeko 2018

たぶん私は何かが足りないんだけど、あなたは、偶然、意味しましたmax(counter, FACTOR)min(counter, FACTOR)常にFACTORを返しますよね?
WebWanderer

1
min(counter, FACTOR)ウォームアップ期間を説明することがポイントだと思います。それがなければ、FACTOR(またはN、または必要なサンプル数)が1000の場合、正確な結果を得るには、少なくとも1000サンプルが必要になります。 20.を持っている
rharter

係数に達した後でカウントを停止するとよいでしょう。おそらくその方が速いでしょう。
inf3rno

8

Flipの答えは、Muisの答えよりも計算上一貫しています。

二重数値形式を使用すると、Muisアプローチで丸めの問題を確認できます。

ムイスのアプローチ

除算または減算すると、前に保存された値に丸めが表示され、値が変更されます。

ただし、フリップアプローチでは、保存された値が保持され、分割数が減るので、丸めが減り、保存された値に伝播するエラーが最小限になります。追加するものがある場合にのみ追加すると丸めが生じます(Nが大きい場合、追加するものはありません)。

フリップアプローチ

これらの変化は、大きな値の平均をゼロに近づけるときに顕著になります。

スプレッドシートプログラムを使用して結果を表示します。

まず、得られた結果: 結果

A列とB列は、それぞれnとX_nの値です。

C列はFlipアプローチで、D列はMuisアプローチで、結果は平均に格納されます。E列は、計算で使用される中間値に対応します。

偶数の値の平均を示すグラフが次のグラフです。

グラフ

ご覧のとおり、両方のアプローチには大きな違いがあります。


2
実際には答えではありませんが、役立つ情報です。グラフに3行目を追加すると、n個の過去の値の真の平均がさらに良くなるため、2つのアプローチのどちらが最も近いかを確認できます。
jpaugh 2017年

2
@jpaugh:B列は-1.00E + 15と1.00E + 15の間で交互になっているため、Nが偶数の場合、実際の平均は0になります。グラフのタイトルは「部分平均でも」です。これは、質問する3行目が単にf(x)= 0であることを意味します。このグラフは、どちらのアプローチでも、エラーが増え続けていくことを示しています。
18

その通りです。グラフは、両方のアプローチを使用した計算に含まれる大きな数値を使用して伝播されたエラーを正確に示しています。
ホセマヌエルラモス

グラフの凡例の色が間違っています。Muisはオレンジ、Flipは青です。
xmedeko 2018

6

比較のために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;
}


1

Java8の場合:

LongSummaryStatistics movingAverage = new LongSummaryStatistics();
movingAverage.accept(new data);
...
average = movingAverage.getAverage();

あなたも持っていますIntSummaryStatisticsDoubleSummaryStatistics...


2
OPは、Javaでこれを計算する方法についての指針ではなく、アルゴリズムを求めています。
olq_plo

0

上記の回答に基づくきちんとした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)

使用法:

x = RunningAverage()
x(0)
x(2)
x(4)
print(x)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.