Gerçek zamanlı ortalama ve standart sapmanın belirlenmesi


31

Gerçek zamanlı bir uygulama için bir sinyalin ortalama ve standart sapmasını bulmak için ideal yol ne olurdu. Bir sinyal belirli bir süre için ortalamanın 3 standart sapmasından fazla olduğunda bir denetleyiciyi tetikleyebilmek istiyorum.

Özel bir DSP'nin bunu kolayca yapacağını farz ediyorum, ancak çok karmaşık bir şey gerektirmeyen herhangi bir "kısayol" var mı?


Sinyal hakkında bir şey biliyor musun? Sabit mi?

@Tim Diyelim ki durağan. Kendi merakım için durağan olmayan bir sinyalin nedenleri neler olurdu?
jonsca

3
Sabitse, çalışan ortalama ve standart sapmayı hesaplayabilirsiniz. Ortalama ve standart sapma zamanla değişirse işler daha karmaşık olurdu.

Yanıtlar:


36

Jason R'ın cevabında, Knuth'un "Bilgisayar Programlama Sanatı" adlı kitabında tartışılan bir kusur var. 2. Sorun, ortalamanın küçük bir kısmı olan standart bir sapmanız varsa ortaya çıkar: E (x ^ 2) - (E (x) ^ 2) 'nin hesaplanması, hassas hassasiyetten kayan nokta yuvarlama hatalarına maruz kalır.

Bunu bir Python betiğinde de deneyebilirsiniz:

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

Matematik, sonucun negatif olamayacağını öngördüğü için, açıkça hesaplamalı olarak geçerli olmayan bir cevap olarak -128.0 elde ediyorum.

Knuth, koşu ortalamasını ve bunun gibi bir şeye giden standart sapmayı hesaplamak için (mucidin adını hatırlamıyorum) bir yaklaşıma işaret ediyor:

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

ve sonra her adımdan sonra, değeri mortalamadır ve standart sapma , en sevdiğiniz standart sapma tanımına bağlı olarak sqrt(S/n)veya bu şekilde hesaplanabilir sqrt(S/n-1).

Yukarıda yazdığım denklem Knuth'dakiden biraz farklı, ancak hesaplamalı olarak eşdeğer.

Birkaç dakikam olduğunda, yukarıdaki formülü Python'da kodlayacağım ve negatif olmayan bir cevap alacağınızı göstereceğim (umarım doğru değere yakındır).


güncelleme: işte burada.

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)

sonuç:

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}

Hala bir yuvarlama hatası olduğunu not edersiniz, ancak fena değil, oysa naive_statssadece kusar.


düzenleme: Sadece Belisarius'un Knuth algoritmasından bahseden Vikipedi'ye atıfta bulunan yorumunu fark ettim .


1
Örnek kod ile ayrıntılı cevap için +1. Bu yaklaşım, bir kayan nokta uygulamasına ihtiyaç duyulduğunda cevabımda belirtilenlerden daha üstün.
Jason R

1
Bir de bir C ++ uygulaması için bunu kontrol edebilir: johndcook.com/standard_deviation.html
Rui Marques

1
evet, işte bu. Knuth'un kullandığı denklemleri kullanır. Biraz optimize edebilir ve eğer yöntemimi kullanırsanız sonraki yinelemelere karşı ilk yinelemeyi kontrol etmek zorunda kalmazsınız.
Jason S

"Knuth, koşu ortalamasını hesaplamak için bir yaklaşım (mucit ismini hatırlamıyorum) gösteriyor" - bu arada bu Welford'un yöntemi .
Jason S


13

Gerçek zamanlı bir uygulama için bir sinyalin ortalama ve standart sapmasını bulmak için ideal yol ne olurdu. Bir sinyal belirli bir süre için ortalamanın 3 standart sapmasından fazla olduğunda bir denetleyiciyi tetikleyebilmek istiyorum.

Bunun gibi durumlarda doğru yaklaşım tipik olarak üssel olarak ağırlıklandırılmış bir koşu ortalamasını ve standart sapmayı hesaplamaktır. Üstel ağırlıklı ortalamada, ortalama ve varyans tahminleri, en son numuneye doğru önyargılıdır , son saniye boyuncaτ ortalama ve varyans tahminlerini size verir , bu muhtemelen tüm numunelerdeki normal aritmetik ortalamadan ziyade istediğiniz şeydir. gördüğüm.

Frekans alanında, "üssel olarak ağırlıklı bir koşu ortalaması" sadece gerçek bir kutuptur. Zaman alanında uygulamak kolaydır.

Zaman alanı uygulaması

Sinyal karesinin ortalaması ve ortalaması için geçerli tahminler olsun meanve olsun meansq. Her döngüde, bu tahminleri yeni örnekle güncelleyin 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);

Burada , koşu ortalamasının efektif uzunluğunu belirleyen bir sabittir. Nasıl seçilir bir "analiz" aşağıda açıklanmıştır.0<bir<1bir

Zorunlu bir program olarak yukarıda belirtilenler ayrıca bir sinyal akış diyagramı olarak da gösterilebilir:

görüntü tanımını buraya girin

analiz

yben=birxben+(1-bir)yben-1xbenbenybenz

'H(z)=bir1-(1-bir)z-1

IIR filtrelerini kendi bloklarında yoğunlaştırmak için, şema şimdi şöyle görünür:

görüntü tanımını buraya girin

z=esTTfs=1/T1-(1-bir)e-sT=0s=1Tgünlük(1-bir)

bir

bir=1-exp{2πTτ}

Referanslar


1
birbir0 > a > 1

Bu, Jason R'nin yaklaşımına benzer. Bu yöntem daha az doğru ancak biraz daha hızlı ve daha düşük bellek olacaktır. Bu yaklaşım üstel bir pencere kullanarak sona erer.
Ocak'ta 12:44

Woops! Tabii ki demek istedim 0 < a < 1. Sisteminizde örnekleme tmi varsa Tve ortalama bir zaman sabiti istiyorsanız tau, seçin a = 1 - exp (2*pi*T/tau).
nibot

Burada bir yanlışlık olabileceğini düşünüyorum. Tek kutuplu filtrelerin DC'de 0 dB kazancı yoktur ve doğrusal alana bir tane ve kare alana bir tane uyguladığınızdan, kazanç hatası E <x> ve E <x ^ 2> için farklıdır. Cevabımı daha ayrıntılı olarak anlatacağım
Hilmar

DC'de 0 dB kazanç var. Yerine z=1(DC) H(z) = a/(1-(1-a)/z)ve
12'de

5

Daha önce bir gömülü işleme uygulamasında daha önce kullandığım bir yöntem, ilgilenilen sinyalin toplamının ve toplamının toplamının biriktiricilerini muhafaza etmektir:

birx,ben=Σk=0benx[k]=birx,ben-1+x[ben],birx,-1=0

birx2,ben=Σk=0benx2[k]=birx2,ben-1+x2[ben],birx2,-1=0

benben

μ~=birxbenben+1

σ~=birxben2ben+1-μ~2

veya kullanabilirsiniz:

σ~=birxben2ben-μ~2

Tercih ettiğiniz standart sapma tahmin yöntemine bağlı olarak . Bu denklemler varyansın tanımına dayanmaktadır :

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

Bunları geçmişte başarıyla kullandım (her ne kadar sadece standart sapma değil, sapma tahminiyle ilgileniyor olmama rağmen), ancak toplanacaksa akümülatörleri tutmak için kullandığınız sayısal türler konusunda dikkatli olmanız gerekir. uzun bir süre; taşması istemiyorsun.

Düzenleme: Taşma ile ilgili yukarıdaki açıklamaya ek olarak, bunun kayan nokta aritmetik olarak uygulandığında sayısal olarak güçlü bir algoritma olmadığı, tahmin edilen istatistiklerde potansiyel olarak büyük hatalara neden olduğu belirtilmelidir. Bu durumda daha iyi bir yaklaşım için Jason S'nin cevabına bakınız.


1
birx,ben=x[ben]+birx,ben-1, birx,0=x[0]benx

Evet, bu daha iyi. Özyinelemeli uygulamayı daha net yapmak için yeniden yazmaya çalıştım.
Jason R,

2
-1, bunu yapacak yeterli temsilcim olduğunda: Bunun sayısal sorunları var. Bkz. Knuth vol. 2
Jason S

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

2
@JasonS: Kayan noktaya uygulandığında sayısal olarak sağlam bir yöntem olmadığı konusunda hemfikir olmanıza rağmen, tekniğin doğal olarak hatalı olduğunu kabul etmiyorum. Bunu integer aritmetik kullanan bir uygulamada başarıyla kullandığımdan daha açık olmalıydım . Tamsayı (veya kesirli sayıların sabit nokta uygulamaları) aritmetiği, hassasiyet kaybına neden olduğunu belirttiğiniz sorundan muzdarip değildir. Bu bağlamda, numune başına daha az işlem gerektiren uygun bir yöntemdir.
Jason R

3

Yukarıdaki tercih edilen cevaba benzer (Jason S.) ve ayrıca Knut'tan alınan formülden de türetilmiştir (Cilt 2, sayfa 232), ayrıca bir değeri değiştirmek, yani bir adımda bir değeri kaldırmak ve bir değer eklemek için bir formül elde edilebilir. . Testlerime göre, değiştirme iki aşamalı çıkarma / ekleme sürümünden daha iyi hassasiyet sunar.

Aşağıdaki kod Java olduğunu meanve saynı ( "küresel" üye değişkenleri) güncellenmeyen mve sJason'ın yayında yukarıda. Değer count, pencere boyutunu belirtir 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 ve Nibot'un cevabı önemli bir açıdan farklılık gösterir: Jason'ın metodu std dev'i ve tüm sinyal için ortalamayı (y = 0'dan beri) hesaplarken, Nibot'un "çalışan" bir hesaplamasıdır, yani daha yeni numuneleri daha ağır basar. uzak geçmiş.

Uygulama std dev gerektirdiği ve zamanın bir işlevi olduğu anlamına geldiğinden, Nibot'un yöntemi muhtemelen en uygun olanıdır (bu özel uygulama için). Ancak, asıl zor kısım, zamanı doğru ölçen kısmı almak olacaktır. Nibot örneği, basit bir tek kutuplu filtre kullanmaktadır.

E[x]x[n]E[x2]x[n]2

Alçak geçirgen filtre seçimi, sinyaliniz ve tahmininiz için ihtiyaç duyduğunuz zaman çözünürlüğü hakkında bildikleriniz tarafından yönlendirilebilir. Düşük kesme frekansları ve daha yüksek sıra daha iyi doğrulukla sonuçlanır ancak daha yavaş tepki süresi sağlar.

İşleri daha da karmaşıklaştırmak için, doğrusal alana bir filtre ve kare alana bir başka filtre uygulanır. Kare alma, sinyalin spektral içeriğini önemli ölçüde değiştirir, böylece kare alanda farklı bir filtre kullanmak isteyebilirsiniz.

İşte zamanın bir fonksiyonu olarak ortalama, rms ve std dev'in nasıl tahmin edileceğine bir örnek.

%% 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
Cevabımdaki filtre buna karşılık geliyor y1 = filter(a,[1 (1-a)],x);.
12'de

1
Genel istatistik ile çalışan istatistik arasındaki farkın iyi olduğu bir nokta. Uygulamam, etkin bir şekilde yapılabilen hareketli bir pencere üzerinde biriken koşu istatistiklerini hesaplamak için değiştirilebildi (her adımda, pencereden dışarı akan zaman örneğini her akümülatörden çıkardı).
Jason R

nibot, üzgünüm haklısın ve yanılmışım. Bunu hemen düzelteceğim
Hilmar

1
X ve x ^ 2 için farklı filtreleme önerdiği için +1
nibot
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.