with book python numpy matplotlib statistics statsmodels

book - statistics with python pdf



Manera pitónica de detectar valores atípicos en datos de observación unidimensionales (5)

Para los datos dados, quiero establecer los valores atípicos (definidos por 95% de nivel de confianza o 95% de función de cuantil o cualquier cosa que se requiera) como valores nan. Lo que sigue es mi información y el código que estoy usando en este momento. Me alegraría si alguien pudiera explicarme más.

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


Bien, una solución simple también puede ser eliminar algo que está fuera de 2 desviaciones estándar (o 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)


El problema con el uso del percentile es que los puntos identificados como valores atípicos son una función del tamaño de la muestra.

Hay una gran cantidad de formas de probar valores atípicos, y debe reflexionar sobre cómo los clasifica. Idealmente, debe usar información a priori (por ejemplo, "cualquier valor superior o inferior a este valor no es realista porque ...")

Sin embargo, una prueba atípica común, no demasiado irrazonable es eliminar puntos en función de su "desviación media absoluta".

Aquí hay una implementación para el caso N-dimensional (de algún código para un documento aquí: 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

Esto es muy similar a una de mis respuestas anteriores , pero quería ilustrar el efecto del tamaño de muestra en detalle.

Comparemos una prueba atípica basada en percentiles (similar a la respuesta de @TCZhu) con una prueba de desviación absoluta media (MAD) para una variedad de diferentes tamaños de muestra:

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

Observe que el clasificador basado en MAD funciona correctamente independientemente del tamaño de la muestra, mientras que el clasificador basado en percentiles clasifica más puntos cuanto mayor sea el tamaño de la muestra, independientemente de si en realidad son valores atípicos.


He adaptado el código de http://eurekastatistics.com/using-the-median-absolute-deviation-to-find-outliers y da los mismos resultados que Joe Kington, pero usa la distancia L1 en vez de la distancia L2, y tiene soporte para distribuciones asimétricas. El código R original no tenía el multiplicador 0.6745 de Joe, así que también lo agregué para mantener la consistencia dentro de este hilo. No está 100% seguro de si es necesario, pero hace la comparación de manzanas a manzanas.

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


La detección de valores atípicos en datos unidimensionales depende de su distribución

1- Distribución normal :

  1. Los valores de los datos se distribuyen casi por igual en el rango esperado: en este caso, todos los métodos que incluyen media, como el intervalo de confianza de 3 o 2 desviaciones estándar (95% o 99,7%), se utilizan fácilmente para una distribución normal de datos (límite central teorema y distribución muestral de la media de la muestra) .I es un método altamente efectivo. Explicado en Khan Academy statistics y Probability - biblioteca de distribución de muestreo.

Otra forma es el intervalo de predicción si desea un intervalo de confianza de los puntos de datos en lugar de la media.

  1. Los valores de los datos se distribuyen aleatoriamente en un rango : la media puede no ser una representación justa de los datos, porque el promedio se ve fácilmente influenciado por valores atípicos (valores muy pequeños o grandes en el conjunto de datos que no son típicos). La mediana es otra forma de mida el centro de un conjunto de datos numéricos.

    Desviación media absoluta : un método que mide la distancia de todos los puntos desde la mediana en términos de distancia media http://www.itl.nist.gov/div898/handbook/eda/section3/eda35h.htm - tiene una buena explicación como explicado en la respuesta de Joe Kington arriba

2 - Distribución simétrica : nuevamente, la desviación absoluta mediana es un buen método si el cálculo de la puntuación z y el umbral se cambian en consecuencia

Explicación: http://eurekastatistics.com/using-the-median-absolute-deviation-to-find-outliers/

3 - Distribución asimétrica: Doble MAD - Explicación de desviación absoluta media doble en el enlace adjunto anterior

Adjuntando mi código python para referencia:

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


Use np.percentile como @Martin sugirió:

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