para - graficas estadisticas python
¿Existe un numpy incorporado para rechazar valores atípicos de una lista (6)
¿Hay un numpy incorporado para hacer algo como lo siguiente? Es decir, tome una lista d
y devuelva una lista filtered_d
con los elementos periféricos eliminados según una distribución supuesta de los puntos en 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]
Digo ''algo así'' porque la función podría permitir distribuciones variables (poisson, gaussianas, etc.) y umbrales de valores atípicos variables dentro de esas distribuciones (como el m
que he usado aquí).
Algo importante cuando se trata de valores atípicos es que uno debe tratar de usar estimadores tan robustos como sea posible. La media de una distribución estará sesgada por valores atípicos, pero, por ejemplo, la mediana será mucho menor.
Basándose en la respuesta de eumiro:
def reject_outliers(data, m = 2.):
d = np.abs(data - np.median(data))
mdev = np.median(d)
s = d/mdev if mdev else 0.
return data[s<m]
Aquí he reemplazado la media con la mediana más robusta y la desviación estándar con la distancia absoluta a la mediana. Luego escale las distancias por su valor mediano (de nuevo) para que m
esté en una escala relativa razonable.
Basándose en Benjamin''s, utilizando pandas.Series
, y reemplazando MAD con IQR :
def reject_outliers(sr, iq_range=0.5):
pcnt = (1 - iq_range) / 2
qlow, median, qhigh = sr.dropna().quantile([pcnt, 0.50, 1-pcnt])
iqr = qhigh - qlow
return sr[ (sr - median).abs() <= iqr]
Por ejemplo, si establece iq_range=0.6
, los percentiles del rango intercuartílico se convertirán en: 0.20 <--> 0.80
, por lo que se incluirán más valores atípicos.
Este método es casi idéntico al tuyo, simplemente más numpyst (también funciona en matrices numpy solamente):
def reject_outliers(data, m=2):
return data[abs(data - np.mean(data)) < m * np.std(data)]
La respuesta de Benjamin Bannier produce un traspaso cuando la mediana de las distancias desde la mediana es 0, por lo que esta versión modificada me resultó un poco más útil para los casos, como se muestra en el siguiente ejemplo.
def reject_outliers_2(data, m = 2.):
d = np.abs(data - np.median(data))
mdev = np.median(d)
s = d/(mdev if mdev else 1.)
return data[s<m]
Ejemplo:
data_points = np.array([10, 10, 10, 17, 10, 10])
print(reject_outliers(data_points))
print(reject_outliers_2(data_points))
Da:
[[10, 10, 10, 17, 10, 10]] # 17 is not filtered
[10, 10, 10, 10, 10] # 17 is filtered (it''s distance, 7, is greater than m)
Quería hacer algo similar, excepto establecer el número en NaN en lugar de eliminarlo de los datos, ya que si lo quita, cambia la longitud que puede arruinar el trazado (es decir, si solo está eliminando valores atípicos de una columna en una tabla , pero necesita que permanezca igual que las otras columnas para que pueda trazarlas una contra la otra).
Para hacerlo, utilicé las funciones de enmascaramiento 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
Una alternativa es hacer una estimación robusta de la desviación estándar (suponiendo que las estadísticas gaussianas). Buscando calculadoras en línea, veo que el percentil del 90% corresponde a 1.2815σ y el 95% es 1.645σ ( http://vassarstats.net/tabs.html?#z )
Como un simple ejemplo:
import numpy as np
# Create some random numbers
x = np.random.normal(5, 2, 1000)
# Calculate the statistics
print("Mean= ", np.mean(x))
print("Median= ", np.median(x))
print("Max/Min=", x.max(), " ", x.min())
print("StdDev=", np.std(x))
print("90th Percentile", np.percentile(x, 90))
# Add a few large points
x[10] += 1000
x[20] += 2000
x[30] += 1500
# Recalculate the statistics
print()
print("Mean= ", np.mean(x))
print("Median= ", np.median(x))
print("Max/Min=", x.max(), " ", x.min())
print("StdDev=", np.std(x))
print("90th Percentile", np.percentile(x, 90))
# Measure the percentile intervals and then estimate Standard Deviation of the distribution, both from median to the 90th percentile and from the 10th to 90th percentile
p90 = np.percentile(x, 90)
p10 = np.percentile(x, 10)
p50 = np.median(x)
# p50 to p90 is 1.2815 sigma
rSig = (p90-p50)/1.2815
print("Robust Sigma=", rSig)
rSig = (p90-p10)/(2*1.2815)
print("Robust Sigma=", rSig)
El resultado que obtengo es:
Mean= 4.99760520022
Median= 4.95395274981
Max/Min= 11.1226494654 -2.15388472011
Sigma= 1.976629928
90th Percentile 7.52065379649
Mean= 9.64760520022
Median= 4.95667658782
Max/Min= 2205.43861943 -2.15388472011
Sigma= 88.6263902244
90th Percentile 7.60646688694
Robust Sigma= 2.06772555531
Robust Sigma= 1.99878292462
Que está cerca del valor esperado de 2.
Si queremos eliminar puntos por encima / por debajo de 5 desviaciones estándar (con 1000 puntos, esperaríamos 1 valor> 3 desviaciones estándar):
y = x[abs(x - p50) < rSig*5]
# Print the statistics again
print("Mean= ", np.mean(y))
print("Median= ", np.median(y))
print("Max/Min=", y.max(), " ", y.min())
print("StdDev=", np.std(y))
Lo que da:
Mean= 4.99755359935
Median= 4.95213030447
Max/Min= 11.1226494654 -2.15388472011
StdDev= 1.97692712883
No tengo idea de qué enfoque es el más eficiente / robusto