リアルタイムでの平均と標準偏差の決定


31

リアルタイムアプリケーションの信号の平均と標準偏差を見つける理想的な方法は何でしょうか。信号が一定時間平均から3標準偏差以上外れたときにコントローラーをトリガーできるようにしたいと思います。

専用のDSPがこれを非常に簡単に行うと仮定していますが、それほど複雑なものを必要としない「ショートカット」はありますか?


信号について何か知っていますか?静止していますか?

@Tim静止しているとしましょう。私自身の好奇心のために、非定常信号の影響は何でしょうか?
jonsca

3
静止している場合は、単純に移動平均と標準偏差を計算できます。平均と標準偏差が時間とともに変化すると、事態はさらに複雑になります。

回答:


36

Jason Rの答えには欠陥があります。これは、Knuthの「コンピュータプログラミングの芸術」vol。2.平均のごく一部である標準偏差がある場合に問題が発生します。E(x ^ 2)-(E(x)^ 2)の計算は、浮動小数点の丸め誤差に対して深刻な影響を受けます。

Pythonスクリプトでこれを自分で試すこともできます。

ofs = 1e9
A = [ofs+x for x in [1,-1,2,3,0,4.02,5]] 
A2 = [x*x for x in A]
(sum(A2)/len(A))-(sum(A)/len(A))**2

答えとして-128.0が得られますが、数学的には結果が非負であるべきだと予測されているため、明らかに計算上有効ではありません。

Knuthは、次のような移動平均と標準偏差を計算するアプローチ(発明者の名前は覚えていません)を引用しています。

 initialize:
    m = 0;
    S = 0;
    n = 0;

 for each incoming sample x:
    prev_mean = m;
    n = n + 1;
    m = m + (x-m)/n;
    S = S + (x-m)*(x-prev_mean);

そして、各ステップの後、の値mは平均であり、標準偏差は、標準偏差の好みの定義として、sqrt(S/n)またはそれにsqrt(S/n-1)応じて計算できます。

上記の式は、Knuthの式とは少し異なりますが、計算上は同等です。

数分後、上記の式をPythonでコーディングし、否定でない答えが得られることを示します(正しい値に近いことを願っています)。


更新:ここにあります。

test1.py:

import math

def stats(x):
  n = 0
  S = 0.0
  m = 0.0
  for x_i in x:
    n = n + 1
    m_prev = m
    m = m + (x_i - m) / n
    S = S + (x_i - m) * (x_i - m_prev)
  return {'mean': m, 'variance': S/n}

def naive_stats(x):
  S1 = sum(x)
  n = len(x)
  S2 = sum([x_i**2 for x_i in x])
  return {'mean': S1/n, 'variance': (S2/n - (S1/n)**2) }

x1 = [1,-1,2,3,0,4.02,5] 
x2 = [x+1e9 for x in x1]

print "naive_stats:"
print naive_stats(x1)
print naive_stats(x2)

print "stats:"
print stats(x1)
print stats(x2)

結果:

naive_stats:
{'variance': 4.0114775510204073, 'mean': 2.0028571428571427}
{'variance': -128.0, 'mean': 1000000002.0028572}
stats:
{'variance': 4.0114775510204073, 'mean': 2.0028571428571431}
{'variance': 4.0114775868357446, 'mean': 1000000002.0028571}

あなたはまだいくらかの丸め誤差があることに気付くでしょうが、それは悪いことではありませんが、naive_statsただ吐くだけです。


編集:Knuthアルゴリズムについて言及しているWikipediaを引用したBelisariusのコメントに気づいたところです。


1
サンプルコード付きの詳細な回答については+1。このアプローチは、浮動小数点の実装が必要な場合に私の答えで示されたアプローチよりも優れています。
ジェイソンR

1
:一つは、また、C ++の実装のためにこれをチェックするかもしれないjohndcook.com/standard_deviation.html
ルイ・マルケス

1
うん、それだけです。彼はKnuthが使用する正確な方程式を使用します。私の方法を使用すれば、多少最適化でき、最初の反復と後続の反復をチェックする必要がなくなります。
ジェイソンS

「クヌースは、実行平均を計算するためのアプローチ(発明者の名前は覚えていません)を引用しています」- ところで、それはウェルフォードの方法です。
ジェイソンS

誰かが助けることができる場合、これに関連する質問を投稿しました:dsp.stackexchange.com/questions/31812/…-
ジョナサン

13

リアルタイムアプリケーションの信号の平均と標準偏差を見つける理想的な方法は何でしょうか。信号が一定時間平均から3標準偏差以上外れたときにコントローラーをトリガーできるようにしたいと思います。

このような状況での正しいアプローチは、通常、指数関数的に加重された移動平均と標準偏差を計算することです。指数関数的に加重平均では、平均と分散の推定値は、あなたが平均と分散の推定値を与える最新のサンプルに偏っている最後のオーバー秒τではなく、すべての上に通常の算術平均よりも、あなたが望むものはおそらくあり、今まで見たサンプル。

周波数領域では、「指数関数的に重み付けされた移動平均」は、実際の極です。時間領域での実装は簡単です。

時間領域の実装

させるmeanおよびmeansqは、信号の二乗の平均および平均の現在の推定値です。すべてのサイクルで、これらの推定値を新しいサンプルで更新しますx

% update the estimate of the mean and the mean square:
mean = (1-a)*mean + a*x
meansq = (1-a)*meansq + a*(x^2)

% calculate the estimate of the variance:
var = meansq - mean^2;

% and, if you want standard deviation:
std = sqrt(var);

ここで、は、移動平均の有効な長さを決定する定数です。を選択方法については、「分析」で説明します。0<a<1a

上記の命令型プログラムとして表現されているものは、シグナルフロー図として表される場合もあります。

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

分析

上記のアルゴリズムは、計算しますここで、はサンプルの入力で、は出力(平均の推定値)です。これは、シンプルな単極IIRフィルターです。服用変換、我々は、伝達関数を見つける。yi=axi+(1a)yi1xiiyiz

H(z)=a1(1a)z1

IIRフィルターを独自のブロックに凝縮すると、ダイアグラムは次のようになります。

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

連続領域に移動するには、置換ここで、はサンプル時間で、はサンプルレートです。解くと、連続システムの極がにあることがわかります。 T f s = 1 / T 1 1 a e s T = 0 s = 1z=esTTfs=1/T1(1a)esT=0s=1Tlog(1a)

選択:A = 1 - EXP { 2 π Ta

a=1exp{2πTτ}

参照資料


1
あなたはどのように説明してもらえ移動平均の長さを決定しますか?そして、どの値を使用必要がありますか?仕様を満たすことは不可能です。aaa0 > a > 1
ディリップサルワテ

これは、Jason Rのアプローチに似ています。この方法は精度は低くなりますが、少し速くなり、メモリが少なくなります。このアプローチでは、指数ウィンドウを使用します。
シュナーフ

わあ!もちろん、私は意味しました0 < a < 1。システムにサンプリングtmieがTあり、平均化時定数が必要な場合はtau、を選択しますa = 1 - exp (2*pi*T/tau)
-nibot

ここに間違いがあると思います。単極フィルターにはDCで0 dBのゲインがありません。1つのフィルターを線形領域に適用し、1つを二乗領域に適用するため、E <x>とE <x ^ 2>でゲイン誤差が異なります。私の答えでより詳しく説明します
ヒルマー

DCでのゲインは0 dBです。z=1(DC)に代入するH(z) = a/(1-(1-a)/z)と、1が得られます。
nibot12年

5

以前に組み込み処理アプリケーションで使用した方法は、対象の信号の和と二乗和の累算器を維持することです。

Ax,i=k=0ix[k]=Ax,i1+x[i],Ax,1=0

Ax2,i=k=0ix2[k]=Ax2,i1+x2[i],Ax2,1=0

また、上記の方程式で現在の時刻を追跡します(つまり、アキュムレータに追加したサンプルの数に注意してください)。次に、時間でのサンプル平均と標準偏差は次のとおりです。ii

μ~=Axii+1

σ~=Axi2i+1μ~2

または使用できます:

σ~=Axi2iμ~2

どちらの標準偏差推定方法を使用するかによります。これらの方程式は、分散定義に基づいています

σ2=E(X2)(E(X))2

過去にこれらを正常に使用しました(ただし、標準偏差ではなく分散推定のみに関係していました)。長期間; あなたはオーバーフローしたくない。

編集:上記のオーバーフローに関するコメントに加えて、これは浮動小数点演算で実装された場合、数値的に堅牢なアルゴリズムではなく、推定統計に大きなエラーを引き起こす可能性があることに注意する必要があります。その場合のより良いアプローチについては、ジェイソンSの答えを見てください。


1
ように書き直すと、2つの数字を追加するだけでよいことが明確になるでしょう。各ステップで、誰かが各ステップですべての要素を合計するように実装しないようにします。Ax,i=x[i]+Ax,i1, Ax,0=x[0]ix
ローレムイプサム

はい、それはましです。再帰的な実装をより明確にするために書き直そうとしました。
ジェイソンR

2
-1十分な担当者がいる場合:これには数値的な問題があります。Knuth vol。を参照してください。2
ジェイソンS

ここにいくつかのタイプミスがあるようです。平方根記号の下で平均が減算されるのはなぜですか?表示された方程式と一致するには、である必要があります、いいえ?また、この答えに賛成票を投じることはしませんが、このアプローチには数値的な問題がある可能性があることをジェイソンSに同意します。σμ2σ2=E(X2)(E(X))2
ディリップサルワテ

2
@JasonS:この手法には本質的に欠陥があることに同意しませんが、浮動小数点で実装した場合、数値的に堅牢な方法ではないという点に同意します。整数演算を使用したアプリケーションでこれをうまく使用できたことをもっと明確にすべきでした。整数(または小数の固定小数点実装)算術演算は、指摘した精度の低下の問題に悩まされません。そのコンテキストでは、サンプルあたりの操作が少なくて済む適切な方法です。
ジェイソンR

3

上記の好ましい回答(Jason S.)と同様に、Knut(Vol.2、p 232)から得られた式から導き出された、値を置き換える式を導出することもできます。 。私のテストによると、replaceは2ステップの削除/追加バージョンよりも優れた精度を提供します。

以下のコードはJavaでmeanありs、更新されます(「グローバル」メンバー変数)。Jasonの投稿mと同じsです。値countはウィンドウサイズを参照しますn

/**
 * Replaces the value {@code x} currently present in this sample with the
 * new value {@code y}. In a sliding window, {@code x} is the value that
 * drops out and {@code y} is the new value entering the window. The sample
 * count remains constant with this operation.
 * 
 * @param x
 *            the value to remove
 * @param y
 *            the value to add
 */
public void replace(double x, double y) {
    final double deltaYX = y - x;
    final double deltaX = x - mean;
    final double deltaY = y - mean;
    mean = mean + deltaYX / count;
    final double deltaYp = y - mean;
    final double countMinus1 = count - 1;
    s = s - count / countMinus1 * (deltaX * deltaX - deltaY * deltaYp) - deltaYX * deltaYp / countMinus1;
}

3

JasonとNibotの答えは1つの重要な側面で異なります。Jasonの方法は、信号全体のstd devと平均を計算します(y = 0であるため)。遠い過去。

アプリケーションは時間の関数としてstd devとmeanを必要とするため、Nibotの方法が(この特定のアプリケーションにとって)おそらくより適切な方法です。ただし、本当のトリッキーな部分は、時間の重み付け部分を正しくすることです。Nibotの例では、単純な単極フィルターを使用しています。

これを記述する適切な方法は、をフィルタリングすることで推定値を取得し、フィルタリングすることで推定値を取得することです。推定フィルターは通常、ローパスフィルターです。これらのフィルターは、0 Hzで0dBゲインになるようにスケーリングする必要があります。そうでなければ、一定のゲインエラーがあります。x [ n ] E [ x 2 ] x [ n ] 2E[x]x[n]E[x2]x[n]2

ローパスフィルターの選択は、信号について知っていることと、推定に必要な時間分解能によって決まります。カットオフ周波数を低くし、次数を高くすると、精度は向上しますが、応答時間が遅くなります。

さらに複雑にするために、1つのフィルターを線形領域に適用し、別のフィルターを2乗領域に適用します。2乗は信号のスペクトル成分を大幅に変更するため、2乗領域で別のフィルターを使用することもできます。

以下は、時間の関数として平均、rms、および標準偏差を推定する方法の例です。

%% example
fs = 44100; n = fs; % 44.1 kHz sample rate, 1 second
% signal: white noise plus a low frequency drift at 5 Hz)
x = randn(n,1) + sin(2*pi*(0:n-1)'*5/fs);
% mean estimation filter: since we are looking for effects in the 5 Hz range we use maybe a
% 25 Hz filter, 2nd order so it's not too sluggish
[b,a] = butter(2,25*2/fs);
xmeanEst = filter(b,a,x);
% now we estimate x^2, since most frequency double we use twice the bandwidth
[b,a] = butter(2,50*2/fs);
x2Est = filter(b,a,x.^2);
% std deviation estimate
xstd = sqrt(x2Est)-xmeanEst;
% and plot it
h = plot([x, xmeanEst sqrt(x2Est) xstd]);
grid on;
legend('x','E<x>','sqrt(E<x^2>)','Std dev');
set(h(2:4),'Linewidth',2);

1
私の答えのフィルターはに対応していy1 = filter(a,[1 (1-a)],x);ます。
-nibot

1
実行中の統計とサンプル全体の統計の区別に関する良い点。私の実装は、移動ウィンドウ上で累積することで実行統計を計算するように変更できますが、これも効率的に実行できます(各タイムステップで、各アキュムレータからウィンドウから外れた時間サンプルを減算します)。
ジェイソンR

nibot、ごめんなさい、あなたは正しいです、私は間違っていました。これをすぐに修正します
ヒルマー

1
xとx ^ 2に異なるフィルタリングを提案するための+1
nibot
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.