실시간으로 평균 및 표준 편차 결정


31

실시간 애플리케이션을위한 신호의 평균 및 표준 편차를 찾는 이상적인 방법은 무엇입니까? 신호가 특정 시간 동안 평균에서 3 표준 편차 이상일 때 컨트롤러를 트리거 할 수 있기를 원합니다.

전용 DSP가이 작업을 매우 쉽게 수행한다고 가정하지만 너무 복잡한 것이 필요하지 않은 "바로 가기"가 있습니까?


신호에 대해 아는 것이 있습니까? 고정되어 있습니까?

@Tim 움직이지 않는다고 가정 해 봅시다. 내 자신의 호기심에 비 정지 신호의 결과는 무엇입니까?
jonsca

3
정지 상태이면 단순히 평균과 표준 편차를 계산할 수 있습니다. 평균 및 표준 편차가 시간에 따라 변하면 상황이 더 복잡해집니다.

답변:


36

Jason R의 답변에 결함이 있습니다.이 내용은 Knuth의 "Art of Computer Programming"vol. 2. 표준 편차가 평균의 작은 부분 인 경우 문제가 발생합니다. E (x ^ 2)-(E (x) ^ 2)의 계산은 부동 소수점 반올림 오류에 대한 민감도가 높습니다.

파이썬 스크립트에서 직접 시도해 볼 수도 있습니다.

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의 방정식과 약간 다르지만 계산적으로 동일합니다.

몇 분 더 있으면 파이썬에서 위의 수식을 코딩하고 음수가 아닌 대답을 얻습니다 (바람직한 값에 가깝습니다).


업데이트 : 여기 있습니다.

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입니다. 이 방법은 부동 소수점 구현이 필요할 때 내 대답에 표시된 방법보다 우수합니다.
Jason R

1
C ++ 구현을 위해 이것을 확인할 수도 있습니다 : johndcook.com/standard_deviation.html
Rui Marques

1
그렇습니다. 그는 Knuth가 사용하는 정확한 방정식을 사용합니다. 내 방법을 사용하는 경우 다소 최적화하고 초기 반복과 후속 반복을 확인하지 않아도됩니다.
Jason S

"Knuth는 달리기 평균을 계산하기위한 접근 방식 (발명가의 이름을 기억하지 못함)을 인용합니다."- 웰 포드의 방법 입니다.
Jason S

누군가 도울 수 있다면 이것과 관련된 질문을 게시했습니다 : dsp.stackexchange.com/questions/31812/…
Jonathan

13

실시간 애플리케이션을위한 신호의 평균 및 표준 편차를 찾는 이상적인 방법은 무엇입니까? 신호가 특정 시간 동안 평균에서 3 표준 편차 이상일 때 컨트롤러를 트리거 할 수 있기를 원합니다.

이와 같은 상황에서 올바른 접근 방식은 일반적으로 지수 가중 실행 평균 및 표준 편차를 계산하는 것입니다. 지수 가중 평균에서 평균 및 분산의 추정치는 가장 최근의 표본으로 편향되어 마지막 초 동안τ 의 평균 및 분산의 추정치를 제공합니다 . 이는 아마도 모든 표본에 대한 일반적인 산술 평균보다는 원하는 것입니다 지금까지 본.

주파수 영역에서 "지수 적으로 가중 된 실행 평균"은 단순히 실제 극입니다. 시간 영역에서 구현하는 것은 간단합니다.

시간 영역 구현

하자 meanmeansq수 평균의 현재 추정하고 신호의 제곱의 의미. 매주기마다 새로운 표본으로 이러한 추정치를 업데이트하십시오 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);

여기서 은 실행 평균의 유효 길이를 결정하는 상수입니다. a 를 선택 하는 방법 은 아래 "분석"에 설명되어 있습니다.0<a<1a

명령형 프로그램으로 위에서 표현 된 것은 신호 흐름 다이어그램으로 묘사 될 수도 있습니다.

여기에 이미지 설명을 입력하십시오

분석

yi=axi+(1a)yi1xiiyiz

H(z)=a1(1a)z1

IIR 필터를 자체 블록으로 응축하면 다이어그램은 다음과 같습니다.

여기에 이미지 설명을 입력하십시오

z=esTTfs=1/T1(1a)esT=0s=1Tlog(1a)

고르다 a

a=1exp{2πTτ}

참고 문헌


1
aa0 > a > 1

이것은 Jason R의 접근 방식과 유사합니다. 이 방법은 정확도는 떨어지지 만 메모리는 조금 더 빠르고 낮습니다. 이 접근법은 지수 창을 사용하여 끝납니다.
schnarf

웁스! 물론 나는 의미했다 0 < a < 1. 시스템에 샘플링 tmie가 T있고 평균 시간 상수를 원하면 tau을 선택하십시오 a = 1 - exp (2*pi*T/tau).
nibot

여기에 실수가 있다고 생각합니다. 단극 필터는 DC에서 0dB 이득을 갖지 않으며 선형 도메인과 제곱 도메인에서 각각 하나의 필터를 적용하기 때문에 E <x>와 E <x ^ 2>의 이득 오차가 다릅니다. 내 대답에 더 자세히 설명하겠습니다
Hilmar

DC에서 0dB 게인을 갖습니다. z=1(DC)로 대체 H(z) = a/(1-(1-a)/z)하면 1을 얻습니다.
nibot

5

임베디드 처리 응용 프로그램에서 이전에 사용한 방법은 관심 신호의 합과 합의 누산기를 유지하는 것입니다.

에이엑스,나는=케이=0나는엑스[케이]=에이엑스,나는1+엑스[나는],에이엑스,1=0

에이엑스2,나는=케이=0나는엑스2[케이]=에이엑스2,나는1+엑스2[나는],에이엑스2,1=0

나는나는

μ~=에이엑스나는나는+1

σ~=에이엑스나는2나는+1μ~2

또는 다음을 사용할 수 있습니다.

σ~=에이엑스나는2나는μ~2

원하는 표준 편차 추정 방법에 따라 . 이 방정식은 분산정의를 기반으로합니다 .

σ2=이자형(엑스2)(이자형(엑스))2

나는 과거에 이것을 성공적으로 사용했지만 (표준 편차가 아닌 분산 추정에만 관심이 있었지만) 합산하려는 경우 누산기를 유지하는 데 사용하는 숫자 유형에주의해야하지만 오랜 시간; 오버플로를 원하지 않습니다.

편집하다: 오버플로에 대한 위의 주석 외에도 부동 소수점 산술로 구현 할 때 수치 적으로 강력한 알고리즘이 아니며 추정 통계에서 큰 오류가 발생할 수 있습니다. 이 경우 더 나은 접근 방식은 Jason S의 답변을 참조하십시오.


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

예, 더 좋습니다. 재귀 구현을보다 명확하게하기 위해 다시 작성하려고했습니다.
Jason R

2
충분한 담당자가있을 때 -1 : 수치 문제가 있습니다. 크 누스 권 참조. 2
Jason S

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

2
@JasonS : 부동 소수점으로 구현 할 때 수치 적으로 강력한 방법이 아니라는 점에 동의하지만 기술에 본질적으로 결함이 있음에 동의하지 않습니다. 정수 산술 을 사용하는 응용 프로그램에서이를 성공적으로 사용했음을 더 분명하게 알았어 야합니다 . 정수 (또는 소수의 고정 소수점 구현) 산술은 사용자가 지적한 문제로 인해 정밀도 손실이 발생하지 않습니다. 이와 관련하여 샘플 당 더 적은 작업이 필요한 적절한 방법입니다.
Jason R

3

위의 선호 답변 (Jason S.)과 유사하고 Knut (Vol.2, p 232)에서 가져온 수식에서 파생 된 것과 같이 값을 대체하는 수식을 파생시킬 수도 있습니다. 즉, 한 단계에서 값을 제거하고 추가 할 수 있습니다 . 내 테스트에 따르면 replace는 2 단계 제거 / 추가 버전보다 더 나은 정밀도를 제공합니다.

아래의 코드는 자바에, mean그리고 s같은, ( "글로벌"멤버 변수) 업데이 트 ms제이슨의 게시물 위. 값 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의 대답은 한 가지 중요한 측면에서 다릅니다. Jason의 방법은 전체 신호에 대한 표준 편차 및 평균을 계산하지만 (y = 0 이후) Nibot은 "실행 중"계산입니다. 먼 과거.

응용 프로그램에는 표준 개발이 필요하고 시간의 함수로 의미되기 때문에 Nibot의 방법이 아마도이 응용 프로그램에 더 적합 할 것입니다. 그러나 실제 까다로운 부분은 시간 가중치 부분을 올바르게 얻는 것입니다. Nibot의 예제는 간단한 단극 필터를 사용합니다.

이자형[엑스]엑스[]이자형[엑스2]엑스[]2

저역 통과 필터의 선택은 신호에 대해 알고있는 것과 추정에 필요한 시간 분해능에 따라 결정될 수 있습니다. 차단 주파수가 낮고 차수가 높을수록 정확도는 향상되지만 응답 시간은 느려집니다.

더 복잡하게하기 위해 하나의 필터가 선형 도메인에 적용되고 다른 필터가 제곱 도메인에 적용됩니다. 제곱은 신호의 스펙트럼 내용을 크게 변경하므로 제곱 도메인에서 다른 필터를 사용할 수 있습니다.

다음은 시간의 함수로 평균, rms 및 std dev를 추정하는 방법에 대한 예입니다.

%% 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
실행 통계와 전체 샘플의 통계를 구분하는 것이 좋습니다. 효율적으로 수행 할 수있는 이동 창에 누적하여 실행 통계를 계산하도록 구현을 수정할 수 있습니다 (각 시간 단계에서 각 누산기에서 창 밖으로 미끄러지는 시간 샘플을 빼십시오).
Jason R

니봇, 미안하지만 네가 틀렸다. 이것을 바로 수정하겠습니다
Hilmar

1
x와 x ^ 2에 대해 다른 필터링을 제안하는 +1
nibot
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.