Подтвердить что ты не робот

Питонический способ обнаружения выбросов в одномерных данных наблюдения

Для данных данных я хочу установить значения outlier (определенные на уровне 95% confidense или 95% квантильной функции или что-то, что требуется) в качестве значений nan. Ниже приведены мои данные и код, которые я использую прямо сейчас. Я был бы рад, если бы кто-то мог объяснить меня дальше.

import numpy as np, matplotlib.pyplot as plt

data = np.random.rand(1000)+5.0

plt.plot(data)
plt.xlabel('observation number')
plt.ylabel('recorded value')
plt.show()
4b9b3361

Ответ 1

Проблема с использованием percentile заключается в том, что точки, идентифицированные как выбросы, являются функцией вашего размера выборки.

Существует огромное количество способов проверки выбросов, и вы должны подумать о том, как вы их классифицируете. В идеале вы должны использовать априорную информацию (например, "что-то выше/ниже этого значения нереально, потому что..." )

Однако общий, не слишком необоснованный тест на выброс - это удаление точек, основанных на их "среднем абсолютном отклонении".

Здесь реализована реализация для N-мерного случая (из некоторого кода для статьи здесь: https://github.com/joferkington/oost_paper_code/blob/master/utilities.py):

def is_outlier(points, thresh=3.5):
    """
    Returns a boolean array with True if points are outliers and False 
    otherwise.

    Parameters:
    -----------
        points : An numobservations by numdimensions array of observations
        thresh : The modified z-score to use as a threshold. Observations with
            a modified z-score (based on the median absolute deviation) greater
            than this value will be classified as outliers.

    Returns:
    --------
        mask : A numobservations-length boolean array.

    References:
    ----------
        Boris Iglewicz and David Hoaglin (1993), "Volume 16: How to Detect and
        Handle Outliers", The ASQC Basic References in Quality Control:
        Statistical Techniques, Edward F. Mykytka, Ph.D., Editor. 
    """
    if len(points.shape) == 1:
        points = points[:,None]
    median = np.median(points, axis=0)
    diff = np.sum((points - median)**2, axis=-1)
    diff = np.sqrt(diff)
    med_abs_deviation = np.median(diff)

    modified_z_score = 0.6745 * diff / med_abs_deviation

    return modified_z_score > thresh

Это очень похоже на один из моих предыдущих ответов, но я хотел подробно проиллюстрировать эффект размера выборки.

Позвольте сравнить тест на выброс на основе процентиля (аналогичный ответу @CTZhu) с помощью теста медианного абсолютного отклонения (MAD) для разных размеров выборки:

import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

def main():
    for num in [10, 50, 100, 1000]:
        # Generate some data
        x = np.random.normal(0, 0.5, num-3)

        # Add three outliers...
        x = np.r_[x, -3, -10, 12]
        plot(x)

    plt.show()

def mad_based_outlier(points, thresh=3.5):
    if len(points.shape) == 1:
        points = points[:,None]
    median = np.median(points, axis=0)
    diff = np.sum((points - median)**2, axis=-1)
    diff = np.sqrt(diff)
    med_abs_deviation = np.median(diff)

    modified_z_score = 0.6745 * diff / med_abs_deviation

    return modified_z_score > thresh

def percentile_based_outlier(data, threshold=95):
    diff = (100 - threshold) / 2.0
    minval, maxval = np.percentile(data, [diff, 100 - diff])
    return (data < minval) | (data > maxval)

def plot(x):
    fig, axes = plt.subplots(nrows=2)
    for ax, func in zip(axes, [percentile_based_outlier, mad_based_outlier]):
        sns.distplot(x, ax=ax, rug=True, hist=False)
        outliers = x[func(x)]
        ax.plot(outliers, np.zeros_like(outliers), 'ro', clip_on=False)

    kwargs = dict(y=0.95, x=0.05, ha='left', va='top')
    axes[0].set_title('Percentile-based Outliers', **kwargs)
    axes[1].set_title('MAD-based Outliers', **kwargs)
    fig.suptitle('Comparing Outlier Tests with n={}'.format(len(x)), size=14)

main()

enter image description here


enter image description here


enter image description here


enter image description here

Обратите внимание, что классификатор на основе MAD работает правильно независимо от размера выборки, в то время как классификатор, основанный на процентилях, классифицирует больше точек, чем больше размер выборки, независимо от того, действительно ли они являются выбросами.

Ответ 2

Я адаптировал код из http://eurekastatistics.com/using-the-median-absolute-deviation-to-find-outliers, и он дает те же результаты, что и Джо Кингтон, но использует расстояние L1 вместо расстояния L2, и имеет поддержку асимметричных распределений. В исходном R-коде не было множителя Joe 0.6745, поэтому я также добавил, что для согласованности в этом потоке. Не 100% уверены, что это необходимо, но делает сравнение яблок-к-яблокам.

def doubleMADsfromMedian(y,thresh=3.5):
    # warning: this function does not check for NAs
    # nor does it address issues when 
    # more than 50% of your data have identical values
    m = np.median(y)
    abs_dev = np.abs(y - m)
    left_mad = np.median(abs_dev[y <= m])
    right_mad = np.median(abs_dev[y >= m])
    y_mad = left_mad * np.ones(len(y))
    y_mad[y > m] = right_mad
    modified_z_score = 0.6745 * abs_dev / y_mad
    modified_z_score[y == m] = 0
    return modified_z_score > thresh

Ответ 3

Обнаружение выбросов в одномерных данных зависит от его распределения

1 - Нормальное распространение:

  • Значения данных почти равномерно распределены по ожидаемому диапазону: В этом случае вы легко используете все методы, которые включают среднее значение, например, доверительный интервал 3 или 2 стандартных отклонения (95% или 99,7%) соответственно для нормально распределенных данных (центральная предельная теорема и распределение выборки среднего значения выборки). высокоэффективный метод. Объясняется в статистике Академии Хана и вероятностной библиотеке распределения выборки.

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

  1. Значения данных распределены случайным образом в диапазоне: среднее не может быть справедливым представлением данных, поскольку на среднее значение легко влияют выбросы (очень маленькие или большие значения в наборе данных, которые не являются типичными) Медиана - это еще один способ измерения центра набора числовых данных.

    Медиан Абсолютное отклонение - метод, который измеряет расстояние всех точек от медианного по среднему расстоянию http://www.itl.nist.gov/div898/handbook/eda/section3/eda35h.htm - имеет хорошее объяснение, как объяснялось в комментарии Джо Кингтона выше

2 - симметричное распределение: снова среднее абсолтое отклонение является хорошим методом, если соответственно изменить значение и порог z-оценки

Объяснение: http://eurekastatistics.com/using-the-median-absolute-deviation-to-find-outliers/

3 - Асимметричное распределение: Двойной MAD ​​- Абсолютное отклонение с двойной средой Объяснение в приведенной выше ссылке

Присоединение моего кода на Python для справки:

 def is_outlier_doubleMAD(self,points):
    """
    FOR ASSYMMETRIC DISTRIBUTION
    Returns : filtered array excluding the outliers

    Parameters : the actual data Points array

    Calculates median to divide data into 2 halves.(skew conditions handled)
    Then those two halves are treated as separate data with calculation same as for symmetric distribution.(first answer) 
    Only difference being , the thresholds are now the median distance of the right and left median with the actual data median
    """

    if len(points.shape) == 1:
        points = points[:,None]
    median = np.median(points, axis=0)
    medianIndex = (points.size/2)

    leftData = np.copy(points[0:medianIndex])
    rightData = np.copy(points[medianIndex:points.size])

    median1 = np.median(leftData, axis=0)
    diff1 = np.sum((leftData - median1)**2, axis=-1)
    diff1 = np.sqrt(diff1)

    median2 = np.median(rightData, axis=0)
    diff2 = np.sum((rightData - median2)**2, axis=-1)
    diff2 = np.sqrt(diff2)

    med_abs_deviation1 = max(np.median(diff1),0.000001)
    med_abs_deviation2 = max(np.median(diff2),0.000001)

    threshold1 = ((median-median1)/med_abs_deviation1)*3
    threshold2 = ((median2-median)/med_abs_deviation2)*3

    #if any threshold is 0 -> no outliers
    if threshold1==0:
        threshold1 = sys.maxint
    if threshold2==0:
        threshold2 = sys.maxint
    #multiplied by a factor so that only the outermost points are removed
    modified_z_score1 = 0.6745 * diff1 / med_abs_deviation1
    modified_z_score2 = 0.6745 * diff2 / med_abs_deviation2

    filtered1 = []
    i = 0
    for data in modified_z_score1:
        if data < threshold1:
            filtered1.append(leftData[i])
        i += 1
    i = 0
    filtered2 = []
    for data in modified_z_score2:
        if data < threshold2:
            filtered2.append(rightData[i])
        i += 1

    filtered = filtered1 + filtered2
    return filtered

Ответ 4

Используйте np.percentile, как предложил @Martin:

In [33]:

P=np.percentile(A, [2.5, 97.5])
In [34]:

A[(P[0]<A)&(P[1]>A)] #or =>, <= for within 95%
A[(P[0]>A)|(P[1]<A)]=np.nan #to set the outliners to np.nan

Ответ 5

Ну, может быть и простое решение, удаляя что-то вне 2 стандартных отклонений (или 1.96):

def outliers(tmp):
    """tmp is a list of numbers"""
    outs = []
    mean = sum(tmp)/(1.0*len(tmp))
    var = sum((tmp[i] - mean)**2 for i in range(0, len(tmp)))/(1.0*len(tmp))
    std = var**0.5
    outs = [tmp[i] for i in xrange(0, len(tmp)) if abs(tmp[i]-mean) > 1.96*std]
    return outs


lst = [random.randrange(-10, 55) for _ in range(40)]
print lst
print outliers(lst)