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?
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()
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.
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.
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.
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()