web-dev-qa-db-fra.com

Méthode pythonique de détection des valeurs aberrantes dans des données d'observation à une dimension

Pour les données fournies, je souhaite définir les valeurs aberrantes (définies par un niveau de confiance de 95% ou une fonction quantile de 95% ou tout autre élément requis) en tant que valeurs nan. Vous trouverez ci-dessous les données et le code que j'utilise actuellement. Je serais heureux si quelqu'un pouvait m'expliquer davantage.

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()
49
user3410943

Le problème avec percentile est que les points identifiés comme points aberrants sont fonction de la taille de votre échantillon.

Il existe un grand nombre de façons de tester les valeurs aberrantes, et vous devriez réfléchir à la façon dont vous les classifiez. Idéalement, vous devriez utiliser des informations a priori (par exemple, "tout ce qui est au-dessus/au-dessous de cette valeur est irréaliste parce que ...").

Cependant, un critère courant, non trop déraisonnable, consiste à supprimer des points en fonction de leur "écart absolu médian".

Voici une implémentation pour le cas N-dimensionnel (à partir d'un code pour un article ici: 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

Ceci est très similaire à ne de mes réponses précédentes , mais je voulais illustrer l'effet de la taille de l'échantillon en détail.

Comparons un test de valeurs aberrantes basé sur le centile (similaire à la réponse de @ CTZhu) avec un test d'écart médian absolu (MAD) pour diverses tailles d'échantillons:

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

Notez que le classificateur basé sur MAD fonctionne correctement quelle que soit la taille de l'échantillon, tandis que le classificateur basé sur les centiles classe plus de points que la taille de l'échantillon est grande, qu'il s'agisse ou non de valeurs aberrantes.

118
Joe Kington

La détection des valeurs aberrantes dans des données unidimensionnelles dépend de leur distribution

1 - Distribution normale :

  1. Les valeurs des données sont réparties presque également sur la plage attendue: Dans ce cas, vous utilisez facilement toutes les méthodes incluant la moyenne, comme un intervalle de confiance de 3 ou 2. écarts types (95% ou 99,7%) en conséquence pour des données distribuées normalement (théorème de la limite centrale et distribution d’échantillonnage de la moyenne de l’échantillon) .I est une méthode très efficace. Expliqué dans les statistiques de la Khan Academy et Probability - bibliothèque de distribution d'échantillonnage.

Un autre moyen est l’intervalle de prédiction si vous voulez un intervalle de confiance des points de données plutôt que la moyenne.

  1. Les valeurs des données sont distribuées de manière aléatoire sur une plage : la moyenne peut ne pas être une représentation fidèle des données, car la moyenne est facilement influencée par des valeurs aberrantes (très petit). ou des valeurs importantes dans l'ensemble de données qui ne sont pas typiques) La médiane est un autre moyen de mesurer le centre d'un ensemble de données numériques.

    Écart absolu médian - une méthode qui mesure la distance de tous les points à la médiane en termes de distance médiane http: //www.itl .nist.gov/div898/handbook/eda/section3/eda35h.htm - a une bonne explication, comme expliqué dans la réponse de Joe Kington ci-dessus

2 - Distribution symétrique : à nouveau: l'écart médian absolu est une bonne méthode si le calcul du score z et le seuil sont modifiés en conséquence

Explication: http://eurekastatistics.com/using-the-median-absolute-deviation-to-find-outliers/

3 - Distribution asymétrique: Double MAD - Double écart médian absolu Explication dans le lien ci-dessus

Attacher mon code python pour référence:

 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
15
shivangi dhakad

J'ai adapté le code de http://eurekastatistics.com/using-the-median-absolute-deviation-to-find-outliers et donne les mêmes résultats que ceux de Joe Kington, mais utilise L1. distance au lieu de L2 et supporte les distributions asymétriques. Le code R original n’avait pas de multiplicateur de Joe de 0.6745, j’ai donc ajouté cela pour assurer la cohérence de ce fil. Pas sûr à 100% si c'est nécessaire, mais fait la comparaison pomme à pomme.

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
12
sergeyf

Utilisation np.percentile comme l'a suggéré @Martin:

percentiles = np.percentile(data, [2.5, 97.5])

# or =>, <= for within 95%
data[(percentiles[0]<data) & (percentiles[1]>data)]

# set the outliners to np.nan
data[(percentiles[0]>data) | (percentiles[1]<data)] = np.nan
3
CT Zhu

Eh bien, une solution simple peut également être, en supprimant quelque chose qui en dehors de 2 écarts types (ou 1,96):

import random
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 range(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)
2
jimseeve