Отсев резко отличающихся наблюдений в малых выборках
Предлагается программный класс, написанный под .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