实时确定平均值和标准偏差


31

对于实时应用,找到信号平均值和标准偏差的理想方法是什么。我希望能够在信号在一定时间内偏离平均值超过3个标准偏差时触发控制器。

我假设专用DSP可以很容易地做到这一点,但是是否有可能不需要那么复杂的“捷径”?


您对信号一无所知吗?它是静止的吗?

@Tim假设它是固定的。出于我自己的好奇,非平稳信号的后果是什么?
jonsca 2011年

3
如果它是固定的,则可以简单地计算运行平均值和标准偏差。如果平均值和标准偏差随时间变化,则事情将变得更加复杂。

Answers:


36

Jason R的答案有一个缺陷,这一点在Knuth的“计算机编程艺术”一卷中进行了讨论。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只是恶作剧。


编辑:刚刚注意到Belisarius引用Wikipedia的评论,但确实提到了Knuth算法。


1
+1以获取示例代码的详细答案。当需要浮点实现时,这种方法优于我在回答中指出的方法。
杰森R

1
可能还需要检查一下C ++的实现:johndcook.com/standard_deviation.html
Rui Marques

1
是的,就是这样。他使用Knuth使用的精确方程式。您可以进行一些优化,并且避免使用我的方法检查初始迭代和后续迭代的情况。
詹森·S

“ Knuth引用了一种计算连续平均值的方法(我不记得发明者的名字了)” – 顺便说一下,这是Welford的方法
杰森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);

这里是确定移动平均的有效长度的常数。下面的“分析”中介绍了如何选择a0<一种<1个一种

上面表示为命令性程序的内容也可以描述为信号流程图:

在此处输入图片说明

分析

上面的算法计算,其中x i是样本i的输入,而y i是输出(即,平均值的估计)。这是一个简单的单极IIR滤波器。通过z变换,我们发现传递函数H z = aÿ一世=一种X一世+1个-一种ÿ一世-1个X一世一世ÿ一世ž

Hž=一种1个-1个-一种ž-1个

将IIR滤波器压缩到自己的块中,现在该图如下所示:

在此处输入图片说明

ž=ËsŤŤFs=1个/Ť1个-1个-一种Ë-sŤ=0s=1个Ť日志1个-一种

一种

一种=1个-经验值{2πŤτ}

参考文献


1
一种一种0 > a > 1

这类似于Jason R的方法。此方法将不太准确,但会更快一些并且内存更少。这种方法最终使用了指数窗口。
schnarf

哇!当然是我的意思0 < a < 1。如果您的系统具有采样tmie,T并且您想要一个平均时间常数tau,则选择a = 1 - exp (2*pi*T/tau)
nibot 2012年

我认为这里可能有一个错误。单极滤波器在DC处没有0 dB的增益,并且由于您在线性域中应用一个滤波器,在平方域中应用一个滤波器,因此对于E <x>和E <x ^ 2>,增益误差是不同的。我将在回答中详细说明
Hilmar 2012年

它在DC处确实具有0 dB的增益。替换z=1(DC)转换H(z) = a/(1-(1-a)/z),你会得到1
nibot

5

我以前在嵌入式处理应用程序中使用过的一种方法是维护感兴趣信号的和和平方和的累加器:

一种X一世=ķ=0一世X[ķ]=一种X一世-1个+X[一世]一种X-1个=0

一种X2一世=ķ=0一世X2[ķ]=一种X2一世-1个+X2[一世]一种X2-1个=0

一世一世

μ=一种X一世一世+1个

σ=一种X一世2一世+1个-μ2

或者您可以使用:

σ=一种X一世2一世-μ2

取决于您喜欢哪种标准偏差估算方法。这些方程式基于方差定义

σ2=ËX2-ËX2

我过去曾经成功使用过这些方法(尽管我只关注方差估计,而不关注标准偏差),但是如果要进行求和,则必须注意用于保存累加器的数值类型长时间 你不想溢出。

编辑:除了以上关于溢出的注释之外,应该注意的是,这在浮点算术中实现时不是一种数字健壮的算法,可能会导致估计统计信息中的大错误。在这种情况下,请查看Jason S的答案以寻求更好的方法。


1
一种X一世=X[一世]+一种X一世-1个 一种X0=X[0]一世X

是的,那更好。我试图重写以使递归实现更加清晰。
詹森·R

2
当我有足够的代表这样做时为-1:这存在数值问题。见Knuth卷。2
Jason S

σμ2σ2=ËX2-ËX2

2
@JasonS:我不同意该技术固有的缺陷,尽管我同意您的观点,即在浮点数中实现时,它不是一种数值上可靠的方法。我应该更清楚地知道,我已经在使用整数算术的应用程序中成功使用了它。整数(或分数的定点实现)算术不受您指出的会导致精度损失的问题的困扰。在这种情况下,这是一种适合的方法,每个样品需要较少的操作。
杰森R

3

与上面的首选答案(Jason S.)类似,并且也从取自Knut的公式(第2卷,第232页)中得出,也可以导出一个公式来替换值,即一步删除并添加值。根据我的测试,替换提供了比两步删除/添加版本更好的精度。

下面的代码是在Java中,means得到更新(“全局”的成员变量),同样ms上述Jason的岗位。该值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的方法计算整个信号的std dev和均值(因为y = 0),而Nibot的计算是“运行中”的计算,即,它权衡比来自样本的样本更强的最近样本遥远的过去。

由于该应用程序似乎需要std dev和均值作为时间的函数,因此Nibot的方法可能更合适(针对此特定应用程序)。但是,真正棘手的部分是正确设置时间加权部分。Nibot的示例使用一个简单的单极点滤波器。

Ë[X]X[ñ]Ë[X2]X[ñ]2

低通滤波器的选择可以由您对信号的了解以及估计所需的时间分辨率来指导。较低的截止频率和较高的阶数将导致精度更高,但响应时间更慢。

为了使事情更加复杂,在线性域中应用了一个滤波器,在平方域中应用了另一个。平方会显着改变信号的频谱内容,因此您可能需要在平方域中使用其他滤波器。

这是一个有关如何估计均值,均方根和标准差作为时间函数的示例。

%% 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 2012年

1
区分运行统计数据和总体样本统计数据之间的区别。我的实现可以修改为通过在移动窗口上进行累加来计算运行统计信息,这也可以高效地完成(在每个时间步长,从每个累加器中减去刚刚从窗口中滑出的时间样本)。
杰森R

nibot,对不起,您是对的,我错了。我会马上纠正这个问题
Hilmar 2012年

1
+1用于建议对x和x ^ 2进行不同的过滤
nibot,2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.