Отсев резко отличающихся наблюдений в малых выборках
автор:
aave
Предлагается программный класс, написанный под .NET на языках C# и VB.NET, который позволяет находить и отсеивать резко отличающиеся значения (грубые погрешности) в малых выборках наблюдений.
Предложенный программный класс основан на методике выявления и исключения промахов из серии измерений, описанный в книге «Обработка результатов наблюдений» О.Н. Кассандровой и В.В. Лебедева 1970 года издания.
1Пример отсева грубых погрешностей в малой выборке
Допустим, у нас есть ряд наблюдений: 12; 11; 10; 11; 13; 9; 10; 12; 17; 9. Нужно исключить значение, которое с вероятностью 95% является ошибкой измерений (грубой погрешностью).
Используя предложенный программный класс, и передав ему исходный массив, мы получим на выходе ряд: 12; 11; 10; 11; 13; 9; 10; 12; 9. Как видно, значение "17" было исключено из ряда как грубое отклонение.
| Исходный ряд | 12 | 11 | 10 | 11 | 13 | 9 | 10 | 12 | 17 | 9 |
| После отсеивания | 12 | 11 | 10 | 11 | 13 | 9 | 10 | 12 | — | 9 |
Если мы построим гистограммы распределения величин исходного ряда (график слева) и ряда после исключения промахов (справа), то увидим следующую картину:

Красной волнистой линией показан нормальный закон распределения. Видно, что значение "17" резко выбивается из нормального закона. Это именно то значение, которое и является грубой погрешностью. Именно его исключил предлагаемый программный класс.
2Программный код для отсевагрубых погрешностей в малой выборке
Основной метод класса – RemoveOutliers(), который получает в качестве входного аргумента массив целых значений (исходный ряд наблюдений), а возвращает массив с исключёнными грубыми погрешностями. Минимальная длина исходной выборки – 3 элемента, максимальная, как правило – не более 52.
Второй используемый метод – StdDeviation() – расчёт среднеквадратичного отклонения. Этот метод также получает на вход массив целых чисел и (опционально) – признак, использовать ли смещённую оценку СКО или несмещённую.
Отсеивание грубых погрешностей на C#
using System;
using System.Linq;
using System.Collections.Generic;
public class Stat
{
public static int[] RemoveOutliers(int[] data)
{
if (data.Length < 3)
{
throw new ArgumentException("Вектор должен содержать хотя бы 3 элемента.");
}
List<int> resList = new List<int>(data);
while (true)
{
double elemMean = ((IEnumerable<int>) resList).Average();
double elemStdDeviation = StdDeviation(resList.ToArray(), false);
int minElem = ((IEnumerable<int>) resList).Min();
int maxElem = ((IEnumerable<int>) resList).Max();
double tau1 = (elemMean - minElem) / elemStdDeviation;
double tauN = (maxElem - elemMean) / elemStdDeviation;
double tauCritical = 0.0;
int numMeasurements = resList.Count;
if (numMeasurements < 40)
{
tauCritical = (2.4 + (((double) numMeasurements) / 57.0)) - (4.0 / ((double) numMeasurements));
}
else
{
tauCritical = 3.0;
}
if ((tau1 <= tauCritical) && (tauN <= tauCritical))
{
return resList.ToArray();
}
if (tau1 >= tauN)
{
int minElemIndex = resList.IndexOf(minElem);
resList.RemoveAt(minElemIndex);
}
else
{
int maxElemIndex = resList.IndexOf(maxElem);
resList.RemoveAt(maxElemIndex);
}
}
}
public static double StdDeviation(int[] data, bool isShifted = false)
{
double meanX = data.Average();
double squaresSum = 0.0;
foreach (int currentX in data)
{
double s = currentX - meanX;
squaresSum += s * s;
}
int n = data.Length - 1;
if (isShifted)
{
n++;
}
return Math.Sqrt(squaresSum / ((double) n));
}
}
Тот же код, переписанный на VB.NET, представлен ниже.
Отсеивание грубых погрешностей на VB.NET
Imports System
Imports System.Linq
Imports System.Collections.Generic
Public Class Stat
''' <summary>
''' Исключает из малой выборки (от 3-х элементов и выше) данные с резко отличающимися значениями (грубые погрешности).
''' </summary>
''' <param name="data">Массив данных, которые нужно проверить на предмет резко отличающихся значений. Минимальный набор – 3 элемента, максимальный – 52.</param>
''' <returns>Массив данных без резко отличающихся значений.</returns>
''' <remarks>Есть массив значений <paramref name="data">data()</paramref>. Считается, что значения элементов подчинены нормальному закону распределения. Необходимо исключить из массива данные с резко отличающимися значениями.</remarks>
Public Shared Function RemoveOutliers(ByVal data() As Integer) As Integer()
If data.Length < 3 Then
Throw New ArgumentException("Выборка должна содержать хотя бы 3 элемента.")
End If
Dim resList As New List(Of Integer)(data)
Do
'Определяем оценки среднего значения и среднеквадратичного отклонения, минимальное и максимальное значения:
Dim elemMean As Double = resList.Average
Dim elemStdDeviation As Double = StdDeviation(resList.ToArray)
Dim minElem As Integer = resList.Min
Dim maxElem As Integer = resList.Max
'Определяем критическое значение тау-статистики:
Dim tauCritical As Double = 0.0
Dim numMeasurements As Integer = resList.Count
If numMeasurements < 40 Then
tauCritical = 2.4 + numMeasurements / 57 - 4.0 / numMeasurements
Else
tauCritical = 3
End If
'Для найденных экстремальных значений определяем тау-статистики:
Dim tau1 As Double = (elemMean - minElem) / elemStdDeviation
Dim tauN As Double = (maxElem - elemMean) / elemStdDeviation
'Проверяем вектор на наличие резко отличающихся значений:
If (tau1 > tauCritical) OrElse (tauN > tauCritical) Then
'Удаляем экстремальный элемент:
If tau1 >= tauN Then
Dim minElemIndex As Integer = resList.IndexOf(minElem)
resList.RemoveAt(minElemIndex)
Else
Dim maxElemIndex As Integer = resList.IndexOf(maxElem)
resList.RemoveAt(maxElemIndex)
End If
Else
Return resList.ToArray
End If
Loop
End Function
''' <summary>
''' Возвращает среднеквадратическое отклонение последовательности.
''' </summary>
''' <param name="data">Массив данных, по которым вычисляется СКО.</param>
''' <param name="isShifted">Нужна ли несмещённая или смещённая оценка СКО. По умолчанию – несмещённая.</param>
Public Shared Function StdDeviation(ByVal data As Integer(), Optional ByVal isShifted As Boolean = False) As Double
Dim meanX As Double = data.Average
Dim squaresSum As Double = 0
For Each currentX As Integer In data
Dim s As Double = currentX - meanX
squaresSum += s * s
Next
Dim n As Integer = data.Length - 1
If isShifted Then n += 1
Dim res As Double = System.Math.Sqrt(squaresSum / n)
Return res
End Function
End Class
