134 votes

Existe-t-il un utilitaire numpy pour rejeter les valeurs aberrantes d'une liste ?

Existe-t-il un buildin numpy pour faire quelque chose comme ce qui suit ? C'est-à-dire, prendre une liste d et retourner une liste filtered_d en éliminant tous les éléments aberrants sur la base d'une distribution supposée des points dans d .

import numpy as np

def reject_outliers(data):
    m = 2
    u = np.mean(data)
    s = np.std(data)
    filtered = [e for e in data if (u - 2 * s < e < u + 2 * s)]
    return filtered

>>> d = [2,4,5,1,6,5,40]
>>> filtered_d = reject_outliers(d)
>>> print filtered_d
[2,4,5,1,6,5]

Je dis "quelque chose comme" parce que la fonction pourrait permettre de varier les distributions (poisson, gaussienne, etc.) et de varier les seuils des valeurs aberrantes au sein de ces distributions (comme la fonction m que j'ai utilisé ici).

3voto

Alex S Points 434

Je voulais faire quelque chose de similaire, mais en mettant le nombre à NaN plutôt qu'en le supprimant des données, car si vous le supprimez, vous modifiez la longueur, ce qui peut perturber le traçage (par exemple, si vous ne supprimez que les valeurs aberrantes d'une colonne dans un tableau, mais que vous avez besoin qu'elle reste la même que les autres colonnes pour pouvoir les tracer les unes par rapport aux autres).

Pour ce faire, j'ai utilisé les fonctions de masquage de numpy :

def reject_outliers(data, m=2):
    stdev = np.std(data)
    mean = np.mean(data)
    maskMin = mean - stdev * m
    maskMax = mean + stdev * m
    mask = np.ma.masked_outside(data, maskMin, maskMax)
    print('Masking values outside of {} and {}'.format(maskMin, maskMax))
    return mask

3voto

Losses Don Points 1

Je voudrais fournir deux méthodes dans cette réponse, la solution basée sur le "z score" et la solution basée sur "IQR".

Le code fourni dans cette réponse fonctionne sur les deux dim simples. numpy et de multiples numpy le tableau.

Importons d'abord quelques modules.

import collections
import numpy as np
import scipy.stats as stat
from scipy.stats import iqr

Méthode basée sur le score z

Cette méthode permet de vérifier si le nombre se situe en dehors des trois écarts types. Sur la base de cette règle, si la valeur est aberrante, la méthode retournera true, sinon, elle retournera false.

def sd_outlier(x, axis = None, bar = 3, side = 'both'):
    assert side in ['gt', 'lt', 'both'], 'Side should be `gt`, `lt` or `both`.'

    d_z = stat.zscore(x, axis = axis)

    if side == 'gt':
        return d_z > bar
    elif side == 'lt':
        return d_z < -bar
    elif side == 'both':
        return np.abs(d_z) > bar

Méthode basée sur l'IQR

Cette méthode testera si la valeur est inférieure à q1 - 1.5 * iqr ou supérieure à q3 + 1.5 * iqr qui est similaire à la méthode de traçage de SPSS.

def q1(x, axis = None):
    return np.percentile(x, 25, axis = axis)

def q3(x, axis = None):
    return np.percentile(x, 75, axis = axis)

def iqr_outlier(x, axis = None, bar = 1.5, side = 'both'):
    assert side in ['gt', 'lt', 'both'], 'Side should be `gt`, `lt` or `both`.'

    d_iqr = iqr(x, axis = axis)
    d_q1 = q1(x, axis = axis)
    d_q3 = q3(x, axis = axis)
    iqr_distance = np.multiply(d_iqr, bar)

    stat_shape = list(x.shape)

    if isinstance(axis, collections.Iterable):
        for single_axis in axis:
            stat_shape[single_axis] = 1
    else:
        stat_shape[axis] = 1

    if side in ['gt', 'both']:
        upper_range = d_q3 + iqr_distance
        upper_outlier = np.greater(x - upper_range.reshape(stat_shape), 0)
    if side in ['lt', 'both']:
        lower_range = d_q1 - iqr_distance
        lower_outlier = np.less(x - lower_range.reshape(stat_shape), 0)

    if side == 'gt':
        return upper_outlier
    if side == 'lt':
        return lower_outlier
    if side == 'both':
        return np.logical_or(upper_outlier, lower_outlier)

Enfin, si vous souhaitez filtrer les valeurs aberrantes, utilisez un filtre numpy sélecteur.

Passez une bonne journée.

3voto

K. Foe Points 31

Considérez que toutes les méthodes ci-dessus échouent lorsque l'écart-type devient très important en raison d'énormes valeurs aberrantes.

( De même que le calcul de la moyenne échoue et qu'il faut plutôt calculer la médiane. Bien que la moyenne soit "plus sujette à une telle erreur que le stdDv". )

Vous pouvez essayer d'appliquer votre algorithme de manière itérative ou vous filtrez en utilisant l'écart interquartile : (ici "facteur" se rapporte à un intervalle n*sigma, mais seulement lorsque vos données suivent une distribution gaussienne)

import numpy as np

def sortoutOutliers(dataIn,factor):
    quant3, quant1 = np.percentile(dataIn, [75 ,25])
    iqr = quant3 - quant1
    iqrSigma = iqr/1.34896
    medData = np.median(dataIn)
    dataOut = [ x for x in dataIn if ( (x > medData - factor* iqrSigma) and (x < medData + factor* iqrSigma) ) ] 
    return(dataOut)

1voto

fpdx Points 8

Ici, je trouve les aberrations dans x et les remplacer par la médiane d'une fenêtre de points ( win ) autour d'eux (en prenant de la réponse de Benjamin Bannier l'écart médian)

def outlier_smoother(x, m=3, win=3, plots=False):
    ''' finds outliers in x, points > m*mdev(x) [mdev:median deviation] 
    and replaces them with the median of win points around them '''
    x_corr = np.copy(x)
    d = np.abs(x - np.median(x))
    mdev = np.median(d)
    idxs_outliers = np.nonzero(d > m*mdev)[0]
    for i in idxs_outliers:
        if i-win < 0:
            x_corr[i] = np.median(np.append(x[0:i], x[i+1:i+win+1]))
        elif i+win+1 > len(x):
            x_corr[i] = np.median(np.append(x[i-win:i], x[i+1:len(x)]))
        else:
            x_corr[i] = np.median(np.append(x[i-win:i], x[i+1:i+win+1]))
    if plots:
        plt.figure('outlier_smoother', clear=True)
        plt.plot(x, label='orig.', lw=5)
        plt.plot(idxs_outliers, x[idxs_outliers], 'ro', label='outliers')                                                                                                                    
        plt.plot(x_corr, '-o', label='corrected')
        plt.legend()

    return x_corr

enter image description here

1voto

whoisraibolt Points 108

Tant de réponses, mais j'en ajoute une nouvelle qui peut être utile à l'auteur ou même aux autres utilisateurs.

Vous pourriez utiliser le Filtre Hampel . Mais vous devez travailler avec Series .

Filtre Hampel renvoie le Indices de valeurs aberrantes vous pouvez alors les supprimer de la liste des Series puis le reconvertir en un List .

Pour utiliser Filtre Hampel vous pouvez facilement installer le paquet avec pip :

pip install hampel

Utilisation :

# Imports
from hampel import hampel
import pandas as pd

list_d = [2, 4, 5, 1, 6, 5, 40]

# List to Series
time_series = pd.Series(list_d)

# Outlier detection with Hampel filter
# Returns the Outlier indices
outlier_indices = hampel(ts = time_series, window_size = 3)

# Drop Outliers indices from Series
filtered_d = time_series.drop(outlier_indices)

filtered_d.values.tolist()

print(f'filtered_d: {filtered_d.values.tolist()}')

Et le résultat sera :

filtré_d : [2, 4, 5, 1, 6, 5]

Où, ts est un pandas Series et window_size est une taille de fenêtre totale sera calculée comme suit 2 * window_size + 1 .

Pour cette série, j'ai mis window_size avec la valeur 3 .

L'avantage de travailler avec Series, c'est qu'il est possible de générer des graphiques :

# Imports
import matplotlib.pyplot as plt

plt.style.use('seaborn-darkgrid')

# Plot Original Series
time_series.plot(style = 'k-')
plt.title('Original Series')
plt.show()

# Plot Cleaned Series
filtered_d.plot(style = 'k-')
plt.title('Cleaned Series (Without detected Outliers)')
plt.show()

Et le résultat sera :

enter image description here enter image description here

Pour en savoir plus sur Filtre Hampel Je vous recommande les lectures suivantes :

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X