Determinación de la media y la desviación estándar en tiempo real


31

¿Cuál sería la forma ideal de encontrar la media y la desviación estándar de una señal para una aplicación en tiempo real? Me gustaría poder activar un controlador cuando una señal estuvo a más de 3 desviaciones estándar de la media durante un cierto período de tiempo.

Supongo que un DSP dedicado haría esto con bastante facilidad, pero ¿hay algún "atajo" que no requiera algo tan complicado?


¿Sabes algo sobre la señal? ¿Es estacionario?

@Tim Digamos que es estacionario. Para mi propia curiosidad, ¿cuáles serían las ramificaciones de una señal no estacionaria?
jonsca

3
Si es estacionario, simplemente puede calcular una media y una desviación estándar. Las cosas serían más complicadas si la media y la desviación estándar variaran con el tiempo.

Respuestas:


36

Hay una falla en la respuesta de Jason R, que se discute en el vol. "Arte de la programación de computadoras" de Knuth. 2. El problema surge si tiene una desviación estándar que es una pequeña fracción de la media: el cálculo de E (x ^ 2) - (E (x) ^ 2) sufre de una severa sensibilidad a los errores de redondeo de coma flotante.

Incluso puedes probar esto tú mismo en un script de 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

Obtengo -128.0 como respuesta, lo que claramente no es computacionalmente válido, ya que las matemáticas predicen que el resultado no debería ser negativo.

Knuth cita un enfoque (no recuerdo el nombre del inventor) para calcular la media de carrera y la desviación estándar que es algo así:

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

y luego, después de cada paso, el valor de mes la media, y la desviación estándar se puede calcular como sqrt(S/n)o sqrt(S/n-1)según cuál sea su definición favorita de desviación estándar.

La ecuación que escribo arriba es ligeramente diferente a la de Knuth, pero es computacionalmente equivalente.

Cuando tenga unos minutos más, codificaré la fórmula anterior en Python y mostraré que obtendrás una respuesta no negativa (que con suerte está cerca del valor correcto).


actualización: aquí está.

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)

resultado:

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}

Notarás que todavía hay algún error de redondeo, pero no está mal, mientras que naive_statssolo vomita.


editar: Acabo de notar el comentario de Belisario citando Wikipedia que menciona el algoritmo de Knuth.


1
+1 para la respuesta detallada con código de ejemplo. Este enfoque es superior al indicado en mi respuesta cuando se necesita una implementación de punto flotante.
Jason R

1
También se puede verificar esto para una implementación de C ++: johndcook.com/standard_deviation.html
Rui Marques

1
Sí, eso es todo. Él usa las ecuaciones exactas que Knuth usa. Puede optimizar un poco y evitar tener que verificar la iteración inicial frente a las iteraciones posteriores si utiliza mi método.
Jason S

"Knuth cita un enfoque (no recuerdo el nombre del inventor) para calcular la media de carrera" , por cierto, es el método de Welford .
Jason S

He publicado una pregunta relacionada con esto si alguien puede ayudar: dsp.stackexchange.com/questions/31812/…
Jonathan

13

¿Cuál sería la forma ideal de encontrar la media y la desviación estándar de una señal para una aplicación en tiempo real? Me gustaría poder activar un controlador cuando una señal estuvo a más de 3 desviaciones estándar de la media durante un cierto período de tiempo.

τ

En el dominio de la frecuencia, un "promedio de ejecución ponderado exponencialmente" es simplemente un polo real. Es simple de implementar en el dominio del tiempo.

Implementación de dominio de tiempo

Sea meany meansqsea ​​las estimaciones actuales de la media y la media del cuadrado de la señal. En cada ciclo, actualice estas estimaciones con la nueva muestra 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

Lo que se expresa arriba como un programa imperativo también se puede representar como un diagrama de flujo de señal:

ingrese la descripción de la imagen aquí

Análisis

yi=axi+(1a)yi1xiiyiz

H(z)=a1(1a)z1

Condensando los filtros IIR en sus propios bloques, el diagrama ahora se ve así:

ingrese la descripción de la imagen aquí

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

a

a=1exp{2πTτ}

Referencias


1
aa0 > a > 1

Esto es similar al enfoque de Jason R. Este método será menos preciso pero un poco más rápido y menor en memoria. Este enfoque termina usando una ventana exponencial.
schnarf

Woops! Por supuesto que quise decir 0 < a < 1. Si su sistema tiene tmie de muestreo Ty desea una constante de tiempo promedio tau, elija a = 1 - exp (2*pi*T/tau).
nibot

Creo que puede haber un error aquí. Los filtros unipolares no tienen ganancia de 0 dB en DC y dado que está aplicando un filtro en el dominio lineal y uno en el dominio cuadrado, el error de ganancia es diferente para E <x> y E <x ^ 2>. Explicaré más en mi respuesta
Hilmar el

Tiene 0 dB de ganancia en DC. Sustituya z=1(DC) en H(z) = a/(1-(1-a)/z)y obtendrá 1.
nibot

5

Un método que he usado antes en una aplicación de procesamiento integrada es mantener acumuladores de la suma y la suma de los cuadrados de la señal de interés:

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

o puedes usar:

σ~=Axi2iμ~2

dependiendo del método de estimación de desviación estándar que prefiera . Estas ecuaciones se basan en la definición de la varianza :

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

Los he usado con éxito en el pasado (aunque solo me preocupaba la estimación de la varianza, no la desviación estándar), aunque debe tener cuidado con los tipos numéricos que usa para mantener los acumuladores si va a sumar un largo periodo de tiempo; No quieres desbordamiento.

Editar: además del comentario anterior sobre el desbordamiento, debe tenerse en cuenta que este no es un algoritmo numéricamente robusto cuando se implementa en aritmética de punto flotante, lo que puede causar grandes errores en las estadísticas estimadas. Mire la respuesta de Jason S para un mejor enfoque en ese caso.


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

Sí eso es mejor. Traté de reescribir para hacer que la implementación recursiva sea más clara.
Jason R

2
-1 cuando tengo suficiente representante para hacerlo: esto tiene problemas numéricos. Ver Knuth vol. 2
Jason S

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

2
@JasonS: No estoy de acuerdo con que la técnica sea inherentemente defectuosa, aunque estoy de acuerdo con su punto de que no es un método numéricamente robusto cuando se implementa en coma flotante. Debería haber sido más claro que he usado esto con éxito en una aplicación que usa aritmética de enteros . La aritmética de enteros (o implementaciones de punto fijo de números fraccionales) no sufre el problema que señaló que causa la pérdida de precisión. En ese contexto, es un método adecuado que requiere menos operaciones por muestra.
Jason R

3

Similar a la respuesta preferida anterior (Jason S.), y también derivada de la fórmula tomada de Knut (Vol.2, p 232), también se puede derivar una fórmula para reemplazar un valor, es decir, eliminar y agregar un valor en un solo paso . Según mis pruebas, el reemplazo ofrece una mejor precisión que la versión de quitar / agregar de dos pasos.

El siguiente código está en Java meany sse actualiza (variables miembro "globales"), igual que my más sarriba en la publicación de Jason. El valor se countrefiere al tamaño de la ventana 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

La respuesta de Jason y Nibot difiere en un aspecto importante: el método de Jason calcula el desarrollo estándar y la media de toda la señal (ya que y = 0), mientras que el de Nibot es un cálculo "en ejecución", es decir, pesa muestras más recientes más fuertes que las muestras de pasado lejano.

Dado que la aplicación parece requerir std dev y mean en función del tiempo, el método de Nibot es probablemente el más apropiado (para esta aplicación específica). Sin embargo, la parte realmente difícil será obtener la parte de ponderación de tiempo correcta. El ejemplo de Nibot usa un filtro simple de un polo.

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

La elección del filtro de paso bajo puede guiarse por lo que sabe sobre su señal y la resolución de tiempo que necesita para su estimación. Las frecuencias de corte más bajas y el orden más alto resultarán en una mejor precisión pero un tiempo de respuesta más lento.

Para complicar más las cosas, se aplica un filtro en el dominio lineal y otro en el dominio cuadrado. La cuadratura cambia significativamente el contenido espectral de la señal, por lo que es posible que desee utilizar un filtro diferente en el dominio cuadrado.

Aquí hay un ejemplo sobre cómo estimar la media, rms y std dev en función del tiempo.

%% 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
El filtro en mi respuesta corresponde a y1 = filter(a,[1 (1-a)],x);.
nibot

1
Buen punto sobre la distinción entre las estadísticas de ejecución y las estadísticas de la muestra general. Mi implementación podría modificarse para calcular estadísticas de ejecución al acumular sobre una ventana en movimiento, lo que también se puede hacer de manera eficiente (en cada paso de tiempo, reste la muestra de tiempo que se deslizó fuera de la ventana de cada acumulador).
Jason R

Nibot, lo siento, tienes razón y me equivoqué. Corregiré esto de inmediato
Hilmar

1
+1 por sugerir diferentes filtros para x y x ^ 2
nibot
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.