Визначення середнього та стандартного відхилень у режимі реального часу


31

Який був би ідеальний спосіб знайти середнє та стандартне відхилення сигналу для додатків у реальному часі. Я хотів би мати можливість запускати контролер, коли сигнал протягом певного часу перевищує середнє відхилення середнього значення.

Я припускаю, що спеціалізований DSP зробить це досить легко, але чи є якийсь "ярлик", який може не вимагати чогось такого складного?


Ви щось знаєте про сигнал? Це стаціонарно?

@Tim Скажімо, що він нерухомий. З моєї власної цікавості, якими були б наслідки нестаціонарного сигналу?
jonsca

3
Якщо він нерухомий, ви можете просто обчислити середнє значення бігу та стандартне відхилення. Речі були б складнішими, якби середнє та стандартне відхилення змінювалися з часом.

5
Дуже пов’язані: en.wikipedia.org/wiki/…
д-р belisarius

Відповіді:


36

У відповіді Джейсона Р є недолік, який обговорюється у "Кмітці комп’ютерного програмування" Кнута. 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 як відповідь, яка явно не обчислювально обгрунтована, оскільки математика передбачає, що результат повинен бути негативним.

Кнут наводить підхід (я не пам’ятаю прізвища винахідника) для обчислення середньої тривалості та середнього відхилення, яка має щось подібне:

 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)залежно від того, яке ваше улюблене визначення стандартного відхилення.

Рівняння, про яке я пишу вище, трохи відрізняється від рівня в Кнуті, але воно обчислювально рівнозначне.

Коли у мене ще кілька хвилин, я зашифрую вищевказану формулу в 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просто тягне.


редагувати: Щойно помітив коментар Велізарія з посиланням на Вікіпедію, де йдеться про алгоритм Кнута.


1
+1 для детальної відповіді з прикладом коду. Цей підхід перевершує той, який вказаний у моїй відповіді, коли потрібна реалізація з плаваючою комою.
Джейсон Р

1
Можна також перевірити це на реалізацію C ++: johndcook.com/standard_deviation.html
Rui Marques

1
так, це все. Він використовує точні рівняння, які використовує Кнут. Ви можете дещо оптимізувати і уникати необхідності перевіряти початкову ітерацію та наступні ітерації, якщо ви використовуєте мій метод.
Jason S

"Кнут цитує підхід (я не пам’ятаю прізвища винахідника) для обчислення середнього бігу" - це , до речі , метод Велфорда .
Jason S

Я опублікував питання, пов’язане з цим, якщо хтось може допомогти: dsp.stackexchange.com/questions/31812/…
Джонатан

13

Який був би ідеальний спосіб знайти середнє та стандартне відхилення сигналу для додатків у реальному часі. Я хотів би мати можливість запускати контролер, коли сигнал протягом певного часу перевищує середнє відхилення середнього значення.

Правильний підхід у подібних ситуаціях, як правило, обчислює експоненціально зважене середнє та середнє відхилення. У експоненціально зваженому середньому оцінці середнього значення та дисперсії є упередженими до останньої вибірки, що дає вам оцінку середнього та відхилення протягом останніх секундτ , що, мабуть, те, що ви хочете, а не звичайне середнє арифметичне для всіх зразки, коли-небудь бачені.

У частотній області "експоненціально зважене середнє середнє значення" - це просто справжній полюс. Це просто реалізувати у часовій області.

Впровадження часової області

Нехай 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 у власні блоки, схема тепер виглядає так:

введіть тут опис зображення

Для переходу до безперервного домену робимо підстановку де - час вибірки, а - швидкість вибірки. Розв’язуючи , знаходимо, що неперервна система має полюс при . z=esTTfs=1/T1(1a)esT=0s=1Tlog(1a)

Виберіть :a

a=1exp{2πTτ}

Список літератури


1
Чи не могли б ви пояснити , як визначає довжину змінного середнього? І яке значення слід використовувати? Специфікацію неможливо виконати. aa0 > a > 1
Діліп Сарват

Це схоже на підхід Джейсона Р. Цей метод буде менш точним, але трохи швидшим і меншим на пам’ять. Цей підхід закінчується використанням експонентного вікна.
шнарф

Вупи! Звичайно, я мав на увазі 0 < a < 1. Якщо у вашій системі є вибірки tmie, Tі ви хочете константа часу усереднення tau, тоді виберіть a = 1 - exp (2*pi*T/tau).
nibot

Я думаю, що тут може бути помилка. Однополюсні фільтри не мають коефіцієнта посилення 0 дБ при постійному струмі, і оскільки ви застосовуєте один фільтр у лінійній області та один у квадратній області, помилка посилення для E <x> та E <x ^ 2>. Я детальніше докладу детальніше у своїй відповіді
Гільмар

У нього посилення 0 дБ при постійному струмі. Замініть z=1(DC) H(z) = a/(1-(1-a)/z)і ви отримаєте 1.
nibot

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

Раніше я їх успішно використовував (хоча я стосувався лише оцінки дисперсії, а не стандартного відхилення), хоча вам потрібно бути обережним щодо числових типів, які ви використовуєте для утримування акумуляторів, якщо збираєтесь підсумовувати тривалий проміжок часу; ви не хочете переповнення.

Редагувати: Окрім вищезазначеного коментаря щодо переповнення, слід зазначити, що це не чисельно надійний алгоритм, який реалізується в арифметиці з плаваючою комою, що може спричинити великі помилки в оціночній статистиці. Подивіться на відповідь Джейсона С. щодо кращого підходу в цьому випадку.


1
Можливо, переписавши його як це дасть зрозуміти, що потрібно додати лише два числа в кожен крок, щоб хтось не реалізував це як підсумовування всіх елементів на кожному кроці. 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: Я не погоджуюся з тим, що методика є по суті хибною, хоча я згоден з вашою думкою, що це не чисельно надійний метод, який реалізується у плаваючій точці. Я мав би бути більш зрозумілим, що я успішно використовував це в додатку, який використовував цілу арифметику . Арифметика цілочисленних (або реалізованих з фіксованою точкою дробів) не страждає від проблеми, яку ви вказали, що призводить до втрати точності. У цьому контексті це підходящий метод, який вимагає меншої кількості операцій на зразок.
Джейсон Р

3

Подібний до кращої відповіді вище (Jason S.), а також випливає з формули, взятої з Knut (Vol.2, p 232), можна також отримати формулу для заміни значення, тобто видалити та додати значення за один крок . Згідно з моїми тестами, заміна забезпечує кращу точність, ніж двоступінчаста версія видалення / додавання.

Код, наведений нижче, знаходиться на Java, meanі sоновлюйте його ("глобальні" змінні учасника), як 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

Відповідь Джейсона і Нібота відрізняється одним важливим аспектом: метод Джейсона обчислює std dev і середнє значення для всього сигналу (оскільки y = 0), тоді як Nibot - це "пробіжний" розрахунок, тобто він зважує новіші зразки, сильніші за зразки далеке минуле.

Оскільки програма, здається, вимагає std dev і означає як функцію часу, метод Нібота, ймовірно, є більш підходящим (для цієї конкретної програми). Однак справжньою складною частиною буде правильне зважування частини часу. У прикладі Нібота використовується простий однополюсний фільтр.

Правильним способом описати це є те, що ми отримуємо оцінку , фільтруючи і оцінку для , фільтруючи . Фільтри оцінювання - це типово низькочастотні фільтри. Ці фільтри повинні бути масштабовані, щоб мати 0dB при 0 Гц. В іншому випадку виникає постійна помилка посилення.xE[x]E [ x 2 ] x [ n ] 2x[n]E[x2]x[n]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
Хороший момент щодо розмежування поточної статистики та статистики загальної вибірки. Моя реалізація може бути модифікована для обчислення статистики запущених даних, накопичуючи над рухомим вікном, що також можна зробити ефективно (на кожному кроці часу, відняти часовий зразок, який щойно вислизнув з вікна з кожного акумулятора).
Джейсон R

nibot, вибач, що ти маєш рацію, і я помилився. Я виправлю це відразу
Гільмар

1
+1 за те, що пропонуються різні фільтрації для x та x ^ 2
nibot
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.