web-dev-qa-db-fra.com

matplotlib: ignorer les valeurs aberrantes lors du traçage

Je trace des données de divers tests. Parfois, dans un test, il se trouve que j'ai une valeur aberrante (par exemple 0,1), tandis que toutes les autres valeurs sont inférieures de trois ordres de grandeur.

Avec matplotlib, je trace par rapport à la plage [0, max_data_value]

Comment puis-je simplement zoomer sur mes données et ne pas afficher les valeurs aberrantes, ce qui perturberait l'axe des x dans mon tracé?

Dois-je simplement prendre le 95 centile et avoir la plage [0, 95_percentile] sur l'axe des x?

29
Ricky Robinson

Il n'y a pas de "meilleur" test unique pour une valeur aberrante. Idéalement, vous devez incorporer des informations a priori (par exemple, "Ce paramètre ne doit pas être supérieur à x à cause de blah ...").

La plupart des tests de valeurs aberrantes utilisent l'écart absolu médian, plutôt que le 95e centile ou une autre mesure basée sur la variance. Sinon, la variance/stddev calculée sera fortement biaisée par les valeurs aberrantes.

Voici une fonction qui implémente l'un des tests aberrants les plus courants.

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

Comme exemple d'utilisation, vous feriez quelque chose comme ceci:

import numpy as np
import matplotlib.pyplot as plt

# The function above... In my case it's in a local utilities module
from sci_utilities import is_outlier

# Generate some data
x = np.random.random(100)

# Append a few "bad" points
x = np.r_[x, -3, -10, 100]

# Keep only the "good" points
# "~" operates as a logical not operator on boolean numpy arrays
filtered = x[~is_outlier(x)]

# Plot the results
fig, (ax1, ax2) = plt.subplots(nrows=2)

ax1.hist(x)
ax1.set_title('Original')

ax2.hist(filtered)
ax2.set_title('Without Outliers')

plt.show()

enter image description here

58
Joe Kington

Si vous n'êtes pas inquiet de rejeter les valeurs aberrantes comme mentionné par Joe et que ce sont des raisons purement esthétiques, vous pouvez simplement définir les limites de l'axe x de votre intrigue:

plt.xlim(min_x_data_value,max_x_data_value)

Où les valeurs sont les limites que vous souhaitez afficher.

plt.ylim(min,max) fonctionne également pour définir des limites sur l'axe y.

9
Jdog

Je passe généralement les données via la fonction np.clip, Si vous avez une estimation raisonnable de la valeur maximale et minimale de vos données, utilisez-la. Si vous n'avez pas d'estimation raisonnable, l'histogramme des données écrêtées vous montrera la taille des queues, et si les valeurs aberrantes ne sont vraiment que des valeurs aberrantes, la queue devrait être petite.

Ce que je dirige est quelque chose comme ceci:

import numpy as np
import matplotlib.pyplot as plt

data = np.random.normal(3, size=100000)
plt.hist(np.clip(data, -15, 8), bins=333, density=True)

Vous pouvez comparer les résultats si vous modifiez les valeurs min et max dans la fonction d'écrêtage jusqu'à ce que vous trouviez les bonnes valeurs pour vos données.

Example

Dans cet exemple, vous pouvez voir immédiatement que la valeur maximale de 8 n'est pas bonne car vous supprimez beaucoup d'informations significatives. La valeur minimale de -15 devrait être correcte car la queue n'est même pas visible.

Vous pourriez probablement écrire du code qui basé sur cela trouve de bonnes limites qui minimisent les tailles des queues selon une certaine tolérance.

2
Jorge E. Cardona

Je pense que l'utilisation de pandas quantile est utile et beaucoup plus flexible.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

fig = plt.figure()
ax1 = fig.add_subplot(121)
ax2 = fig.add_subplot(122)

pd_series = pd.Series(np.random.normal(size=300)) 
pd_series_adjusted = pd_series[pd_series.between(pd_series.quantile(.05), pd_series.quantile(.95))] 

ax1.boxplot(pd_series)
ax1.set_title('Original')

ax2.boxplot(pd_series_adjusted)
ax2.set_title('Adjusted')

plt.show()

enter image description here

1
Zstack