الگوریتم محاسبه واریانس

الگوریتم Naïve
فرمول محاسبه واریانس جامعه آماری با اندازه N به این صورت است:
فرمول محاسبه تخمین unbiased از واریانس جامعه آماری از یک نمونه محدود از n مشاهده به این صورت می‌باشد:
بنابراین الگوریتم naive برای محاسبه واریانس تخمینی به صورت زیر می‌باشد:
def naive_variance(data):
n = 0
Sum = 0
Sum_sqr = 0
for x in data:
n = n + 1
Sum = Sum + x
Sum_sqr = Sum_sqr + x*x
variance = (Sum_sqr - (Sum*Sum)/n)/(n - 1)
return variance
این الگوریتم به راحتی می‌تواند برای محاسبه واریانس یک جامعه محدود به کار رود: فقط کافیست که در خط آخر به جای تقسیم بر N، بر n − 1 تقسیم گردد.
چون Sum_sqr و (Sum*Sum)/n میتوانند مقدار بسیار نزدیکی داشته باشند، تقریب زدن می‌تواند منجر شود که دقت نتیجه بسیار کمتر از دقت ذاتی موجود در محاسبات ممیز شناور شود. بنابراین این الگوریتم نباید در عمل مورد استفاده قرار گیرد. این موضوع بخصوص در مواردی که انحراف معیار نسبت به میانگین بسیار کوچک است، تشدید می‌شود. اگرچه، این الگوریتم می‌تواند با استفاده از روش میانگین مفروض بهبود یابد.
الگوریتم دومرحله‌ای
یک روش جایگزین، استفاده از فرمول دیگری برای محاسبه واریانس می‌باشد، به این صورت که ابتدا میانگین نمونه را محاسبه میکند:
و سپس مجموع مربعات فاصله از میانگین را بدست می‌آورد:
S برابر با مقدار انحراف معیار است، که با استفاده از شبه کد زیر به دست می‌آید:
def two_pass_variance(data):
n = 0
sum1 = 0
sum2 = 0
for x in data:
n = n + 1
sum1 = sum1 + x
mean = sum1/n
for x in data:
sum2 = sum2 + (x - mean)*(x - mean)
variance = sum2/(n - 1)
return variance
این الگوریتم همواره از لحاظ عددی، سازگار است، مگر برای nهای بزرگ. اگرچه می‌تواند برای نمونه‌هایی که در شرایط زیر صدق می‌کنند، دارای خطا باشد:
1.اکثر داده‌های آن، بسیار نزدیک به میانگین(و نه برابر با آن) باشند.
2.فقط تعداد اندکی از داده‌ها، با فاصله‌ی بسیار زیاد از میانگین باشند..
نتایج هر دو الگوریتم می‌تواند بی‌اندازه، به ترتیب داده‌ها وابسته باشد و می‌تواند نتایج ضعیفی برای مجموعه داده‌های بسیار بزرگ (بخاطر تکرر خطای گرد کردن) داشته باشد. تکنیکهایی همچون مجموع جبران شده می‌تواند تا درجه‌ای برای از بین بردن این خطا مورد استفاده قرار گیرند.
مغایرت جبران شده
نسخه‌ی مجموع جبران شده از الگوریتم فوق بدین صورت است:
def compensated_variance(data):
n = 0
sum1 = 0
for x in data:
n = n + 1
sum1 = sum1 + x
mean = sum1/n
sum2 = 0
sum3 = 0
for x in data:
sum2 = sum2 + (x - mean)**2
sum3 = sum3 + (x - mean)
variance = (sum2 - sum3**2/n)/(n - 1)
return variance
الگوریتم برخط
محاسبه‌ی واریانس در یک مرحله، اغلب می‌تواند بسیار مفید باشد، به عنوان مثال، وقتی داده‌ها در حال جمع‌آوری هستند و فضای کافی برای ذخیره همه داده‌ها نداریم، یا موقعی که هزینه دسترسی به حافظه، بر هزینه محاسبات تاثیر زیادی دارد. برای چنین الگوریتم برخطی، رابطه رخداد مجدد بین کمیتهایی مورد استفاده در محاسبات، مورد نیاز می‌باشد تا محاسبات ما به صورت به روشی سازگار انجام شود.
فرمولهای زیر برای به روزرسانی میانگین و واریانس(تخمینی) داده‌ها، در زمان افزودن عنصر جدید استفاده می‌شود. در اینجا، n نشان دهنده میانگین n نمونه‌ی اول (x 1 , …, x n ) و s 2 n واریانس نمونه و  2 N واریانس جامعه‌ی آماری آنهاست.
همچنین میتوان نشان داد که کمیت مناسبتر برای به روزرسانی مجموع مربعات فاصله از میانگین کنونی ( ) ، با عنوان دراینجا نشان داده شده است:
یک الگوریتم عددی سازگار به صورت زیر داده شده است، که البته میانگین را نیز محاسبه می‌نماید. این الگوریتم، منتسب به Knuth است که او نیز از الگوریتمی از Welford استفاده کرده و به صورت کامل تحلیل نموده‌است. همچنین معمول است که متغیرهای زیر به جای همدیگر استفاده شوند:
and
def online_variance(data):
n = 0
mean = 0
M2 = 0
for x in data:
n = n + 1
delta = x - mean
mean = mean + delta/n
M2 = M2 + delta*(x - mean)
variance = M2/(n - 1)
return variance
این الگوریتم بخاطر کاهش میزان گرد کردن، می‌تواند کاهش دقت کمتری داشته باشد، اما احتمالا نمی‌تواند خیلی بهینه باشد، چرا که عمل تقسیم درون حلقه انجام می‌شود. برای الگوریتم عظیمتر دومرحله‌ای برای محاسبه واریانس، بهتر است ابتدا تخمینی از میانگین را محاسبه کنیم و سپس در ادامه از این الگوریتم استفاده نماییم.
الگوریتم موازی که در ادامه خواهد آمد، نشان می‌دهد که چگونه چندین مجموعه آماری را که به صورت برخط محاسبه می‌شوند، ادغام نمود.
الگوریتم افزایشی وزن دار
این الگوریتم می‌تواند برای کنترل نمونه‌های دارای وزن تعمیم یابد، بدین صورت که میتوان شمارنده n را برای مجموع وزنهایی مشاهده شده تاکنون استفاده کرد. West (1979) این الگوریتم افزایشی را پیشنهاد می‌دهد:
def weighted_incremental_variance(dataWeightPairs):
sumweight = 0
mean = 0
M2 = 0
for x, weight in dataWeightPairs: # Alternatively "for x, weight in zip(data, weights):"
temp = weight + sumweight
delta = x - mean
R = delta * weight / temp
mean = mean + R
M2 = M2 + sumweight * delta * R # Alternatively, "M2 = M2 + weight * delta * (x−mean)"
sumweight = temp
variance_n = M2/sumweight
variance = variance_n * len(dataWeightPairs)/(len(dataWeightPairs) - 1)
الگوریتم موازی
Chan et al. می‌گوید که سومین الگوریتم برخط فوق، نمونه‌ای خاص از الگوریتم است که برای هر تقسیم بندی از نمونه به مجموعه‌های , درست عمل میکند:
این موضوع می‌تواند زمانی مفید باشد که برای مثال، چندین واحد پرداش برای قسمت‌های مختلف از ورودی در نظر گرفته شده باشد.
شیوه Chan روشی برای تخمین میانگین، زمانی که و هر دو بسیار بزرگ باشند، از لحاظ عددی نامطمئن است، چراکه خطای عددی موجود در متناسب با زمانی نیست که . در چنین مواردی، ترجیح داده می‌شود.
مثال
فرض کنید که عملیات ممیز شناور از استاندارد محاسباتی IEEE 754 double-precision استفاده میکند. نمونه‌ی {4، 7، 13، 16 را از یک جامعه آماری نامحدود فرض کنید. براساس این نمونه، میانگین تخمینی جامعه برابر با 10 است و واریانس تخمینی بدون پیشقدر جامعه برابر با 30 است. هر دو الگوریتم اول و دوم این مقادیر را به درستی محاسبه میکند. سپس نمونه { 10 8 + 4, 10 8 + 7, 10 8 + 13, 10 8 + 16 را در نظر بگیرید، که مقدار واریانسی برابر با نمونه‌ی قبلی به ما می‌دهد. الگوریتم دوم این واریانس تخمینی را به درستی محاسبه میکند، اما الگوریتم اول مقدار 29.33333333 را به جای 30 برمی‌گرداند. اگرچه این کمبود دقت قابل تحمل است و به عنوان نقص کوچکی در الگوریتم اول مشاهده می‌شود، اما به سادگی داده‌هایی یافت که نقص اساسی در الگوریتم naive را آشکار میسازد: نمونه‌ی { 10 9 + 4, 10 9 + 7, 10 9 + 13, 10 9 + 16 را در نظر بگیرید. دوباره واریانس تخمینی برابر با 30 می‌باشد که توسط الگوریتم دوم به سادگی قابل محاسبه است، اما این دفعه الگوریتم naive مقدار −170.66666666666666 را به عنوان جواب برمی‌گرداند. این یک مشکل اساسی در الگوریتم اول می‌باشد و بخاطر تقریب زدن فاجعه آمیزی رخ داده است که در مرحله آخر الگوریتم، هنگام تفریق دو عدد مشابه صورت گرفته‌است.
آمارهای رده بالاتر
Terriberry فرمول Chan را برای محاسبه سومین و چهارمین گشتاور مرکزی تعمیم می‌دهد. برای مثال برای تخمین چولگی و کشیدگی مورد نیاز است:
\begin{align
M_{4,X = M_{4,A + M_{4,B - + \delta^4\frac{n_A n_B \left(n_A^2 - n_A n_B + n_B^2\right){n_X^3 \\
- + 6\delta^2\frac{n_A^2 M_{2,B + n_B^2 M_{2,A{n_X^2 + 4\delta\frac{n_AM_{3,B - n_BM_{3,A{n_X \\
\end{align
اینجا برابر با توانهای فاصله با میانه است که نتیجه می‌دهد:
چولگی:
کشیدگی:
برای نسخه افزایشی (مثلا )، به صورت زیر ساده می‌شود:
با نگهداشتن مقدار ، فقط یک عمل تقسیم مورد نیاز است و آمار رده بالاتر می‌تواند بنابراین برای هزینه افزایشی محاسبه شود.
مثال الگوریتم برخط برای کشیدگی به صورت زیر پیاده‌سازی شده است:
def online_kurtosis(data):
n = 0
mean = 0
M2 = 0
M3 = 0
M4 = 0
for x in data:
n1 = n
n = n + 1
delta = x - mean
delta_n = delta / n
delta_n2 = delta_n * delta_n
term1 = delta * delta_n * n1
mean = mean + delta_n
M4 = M4 + term1 * delta_n2 * (n*n - 3*n + 3) + 6 * delta_n2 * M2 - 4 * delta_n * M3
M3 = M3 + term1 * delta_n * (n - 2) - 3 * delta_n * M2
M2 = M2 + term1
kurtosis = (n*M4) / (M2*M2) - 3
return kurtosis
Pébay سپس این نتایج را برای گشتاورهای مرکزی رده-اختیاری، برای موارد افزایشی و دوبه دو تعمیم داد. میتوان فرمولهای مشابهی را برای کوواریانس یافت.
Choi و Sweetman دو روش جایگزین برای محاسبه چولگی و کشیدگی پیشنهاد می‌دهند، که هر کدام می‌تواند مقدار قابل ملاحظه‌ای در حافظه مورد نیاز و زمان CPU در کاربردهای معینی، صرفه جویی کند. روش اول، برای محاسبه گشتاورهای آماری، با جداسازی داده‌ها، گشتاورها را از رابطه هندسی هیستوگرام به دست آمده محاسبه میکند، که به صورت بهینه‌ای تبدیل به یک الگوریتم یک مرحله‌ای برای گشتاورهای بالاتر می‌شود. یکی از مزایای کار این است که محاسبات گشتاورهای آماری می‌تواند با دقت اختیاری محاسبه شود، پس میتوان این دقت را با دقت فرمت انبار داده ویا سخت افزار اندازه‌گیری اصلی، منطبق کرد. هیستوگرام نسبی از یک متغیر تصادفی می‌تواند یک در این راه قراردادی ساخته شود: محدوده مقادیر بالقوه به بازه‌هایی تقسیم می‌شود و تعداد رخدادها در هر بازه شمارش و به گونه‌ای کشیده می‌شود که مساحت هر مستطیل برابر با سهم رخدادهای آن بازه شود:
که و نشاندهنده‌ی میزان فرکانس و فرکانس نسبی در بازه‌ی است و A= \sum_{k=1^{K h(x_k)
\,\Delta x_k مجموع تمام نواحی هیستوگرام است. بعد از نرمال سازی، گشتاور خام و گشتاورهای مرکزی می‌تواند از هیستوگرام نسبی محاسبه شود:
m_n^{(h) = \sum_{k=1^{K x_k^n \, H(x_k) \Delta x_k
= \frac{1{A \sum_{k=1^{K x_k^n \, h(x_k) \Delta x_k
\theta_n^{(h)= \sum_{k=1^{K \Big(x_k-m_1^{(h)\Big)^n \, H(x_k)\Delta x_k
= \frac{1{A \sum_{k=1^{K \Big(x_k-m_1^{(h)\Big)^n \, h(x_k) \Delta x_k
که توان نشان دهنده گشتاورهایی است که از هیستوگرام محاسبه شده‌است. برای بازه‌ی با ضخامت ثابت \Delta
x_k=\Delta x این دو عبارت می‌تواند با استفاده از ساده شود:
m_n^{(h)= \frac{1{I {\sum_{k=1^{K x_k^n \, h(x_k)
\theta_n^{(h)= \frac{1{I{\sum_{k=1^{K \Big(x_k-m_1^{(h)\Big)^n \, h(x_k)
دومین روش Choi و Sweetman یک متدولوژی تحلیلی برای ترکیب گشتاورهای آماری از بخشهای مجزا از یک بازه‌ی زمانی است که گشتاورهای کلی بدست آمده، همان گشتاورهای بازه‌ی زمانی کامل است. این متدولوژی می‌تواند برای محاسبه موازی گشتاورهای آماری با کمک ترکیب گشتاورهای پس آیند(بعدی) ویا ترکیب گشتاورهای آماری محاسبه شده در زمان‌های پشت سرهم، استفاده شود.
اگر تعداد مجموعه از گشتاورهای آماری به این صورت نمایش داده شوند: (\gamma_{0,q,\mu_{q,\sigma^2_{q,\alpha_{3,q,\alpha_{4,q)
\quad که در آن است، سپس هر می‌تواند براساس گشتاور اولیه معادل نمایش داده شود:
\gamma_{n,q= m_{n,q \gamma_{0,q \qquad \quad \textrm{for \quad n=1,2,3,4 \quad \text{ and \quad q = 1,2, \dots ,Q
که به صورت کلی در طول بازه‌ی زمانی بدست آمده‌است یا تعداد نقاطی که ثابت است.
مزیت نمایش گشتاورهای آماری براساس این است که مجموعه میتوانند با عمل جمع ترکیب شوند و محدودیتی در مقدار نیست.
\gamma_{n,c= \sum_{q=1^{Q\gamma_{n,q \quad \quad \textrm{for \quad n=0,1,2,3,4
در اینجا اندیس ترکیب بازه‌های زمانی یا ی ترکیب شده را نشان می‌دهد. این مقادیر ترکیب شده از می‌تواند سپس به صورت معکوس به گشتاورهای خام که تمام بازه‌ی ترکیبی را نشان می‌دهند، تبدیل شوند:
m_{n,c=\frac{\gamma_{n,c{\gamma_{0,c \quad \textrm{for \quad n=1,2,3,4
روابط مشاهده شده بین گشتاورهای خام ( ) و گشتاورهای مرکزی ( )، سپس برای محاسبه گشتاورهای مرکزی ترکیب بازه زمانی مورد استفاده قرار می‌گیرد. در پایان، گشتاورهای آماری بازه‌ی ترکیبی از گشتاورهای مرکزی محاسبه می‌شوند:
\mu_c=m_{1,c
\ \ \ \ \ \sigma^2_c=\theta_{2,c
\ \ \ \ \ \alpha_{3,c=\frac{\theta_{3,c{\sigma_c^3
\ \ \ \ \ \alpha_{4,c={\frac{\theta_{4,c{\sigma_c^4-3
کوواریانس
الگوریتمهای کاملا مشابهی هم می‌تواند برای محاسبه کوواریانس به کار برده شود. الگوریتم naive بدین صورت در می‌آید:
برای الگوریتم فوق میتوان شبه کد زیر را استفاده کرد:
def naive_covariance(data1, data2):
n = len(data1)
sum12 = 0
sum1 = sum(data1)
sum2 = sum(data2)
for i in range(n):
sum12 += data1i*data2i
covariance = (sum12 - sum1*sum2 / n) / n
return covariance
یک الگوریتم دو مرحله‌ای مطمئن‌تر از لحاظ عددی، ابتدا میانه‌های میانگین را محاسبه می‌کند و سپس کوواریانس را به دست می‌آورد:
الگوریتم دومرحله‌ای می‌تواند به صورت زیر نوشته شود:
def two_pass_covariance(data1, data2):
n = len(data1)
mean1 = sum(data1) / n
mean2 = sum(data2) / n
covariance = 0
for i in range(n):
a = data1i - mean1
b = data2i - mean2
covariance += a*b / n
return covariance
یک نسخه‌ی جبرانی با دقت بالاتر، الگوریتم naive را بر روی باقی‌مانده‌ها انجام می‌دهد. مجموع نهایی و باید صفر شود، اما مرحله دوم هرگونه خطای کوچکی را جبران کند.
یک الگوریتم تک مرحله‌ای مطمئنی، مشابه با مورد بالا، وجود دارد، که گشتاورهای مشترک را محاسبه میکند:
عدم تقارن آشکار در تساوی آخر بخاطر این است که ، پس هر دو عبارت به روزرسانی برابرند با: . با محاسبه‌ی میانگین‌ها میتوان به دقت بالاتری دست یافت، سپس بااستفاده از الگوریتم تک مرحله‌ای مطمئن بر روی بقیه استفاده کرد.
بنابراین ما میتوانیم کوواریانس را به این صورت به دست آوریم:
\begin{align
\operatorname{Cov_N(X,Y) = \frac{C_N{N -= \frac{\operatorname{Cov_{N-1(X,Y)\cdot(N-1) + (x_n - \bar x_n)(y_n - \bar y_{n-1){N\\
-= \frac{\operatorname{Cov_{N-1(X,Y)\cdot(N-1) + (y_n - \bar y_n)(x_n - \bar x_{n-1){N\\
-= \frac{\operatorname{Cov_{N-1(X,Y)\cdot(N-1) + \frac{N-1{N(x_n - \bar x_{n-1)(y_n - \bar y_{n-1){N.
\end{align
به همین ترتیب، فرمولی برای ترکیب کوواریانس دو مجموعه برای موازی‌سازی محاسبات وجود دارد