Рейтинг@Mail.ru

Отсев резко отличающихся наблюдений в малых выборках

Предлагается программный класс, написанный под .NET на языках C# и VB.NET, который позволяет находить и отсеивать резко отличающиеся значения (грубые погрешности) в малых выборках наблюдений.

Предложенный класс основан на методике выявления и исключения промахов из серии измерений, описанный в книге «Обработка результатов наблюдений» О.Н. Кассандровой и В.В. Лебедева 1970 года издания.

Пример отсева грубых погрешностей в малой выборке

Допустим, у нас есть ряд наблюдений: 12; 11; 10; 11; 13; 9; 10; 12; 17; 9. Нужно исключить значение, которое с вероятностью 95% является ошибкой измерений (грубой погрешностью).

Используя предложенный программный класс, и передав ему исходный массив, мы получим на выходе ряд: 12; 11; 10; 11; 13; 9; 10; 12; 9. Как видно, значение "17" было исключено из ряда как грубое отклонение.

Если мы построим гистограммы распределения величин исходного ряда (график слева) и ряда после исключения промахов (справа), то увидим следующую картину:

Гистограммы распределения значений исходного ряда наблюдений и после исключения грубой погрешности
Гистограммы распределения значений исходного ряда наблюдений и после исключения грубой погрешности

Красной волнистой линией показан нормальный закон распределения. Видно, что значение "17" резко выбивается из нормального закона. Это именно то значение, которое и является грубой погрешностью. Именно его исключил предлагаемый программный класс.

Программный код для отсева грубых погрешностей в малой выборке

Основной метод класса – 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 resList = new List(data);
    while (true)
    {
      double elemMean = ((IEnumerable) resList).Average();
      double elemStdDeviation = StdDeviation(resList.ToArray(), false);
      int minElem = ((IEnumerable) resList).Min();
      int maxElem = ((IEnumerable) 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:

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
Последнее изменениеПонедельник, 23 Январь 2017 20:57
(1 Голосовать)
Прочитано 818 раз

Поделиться

Оставить комментарий

Убедитесь, что вы вводите (*) необходимую информацию, где нужно
HTML-коды запрещены