Detectar y excluir valores atípicos en el dataframe de Pandas

Tengo un dataframe de pandas con pocas columnas.

Ahora sé que ciertas filas son valores atípicos basados ​​en un determinado valor de columna.

Por ejemplo, las columnas: ‘Vol’ tiene todos los valores alrededor de 12xx y un valor es 4000 (valor atípico).

Ahora me gustaría excluir aquellas filas que tienen la columna ‘Vol’ como esta. Así que, esencialmente, necesito colocar un filtro en el dataframe de manera que seleccionemos todas las filas donde los valores de una determinada columna estén dentro de, por ejemplo, 3 desviaciones estándar de la media.

¿Qué es una manera elegante de lograr esto.

Si tiene varias columnas en su dataframe y desea eliminar todas las filas que tienen valores atípicos en al menos una columna, la siguiente expresión lo haría de una sola vez.

df = pd.DataFrame(np.random.randn(100, 3)) from scipy import stats df[(np.abs(stats.zscore(df)) < 3).all(axis=1)] 

descripción:

  • Para cada columna, primero calcula el puntaje Z de cada valor en la columna, en relación con la media de la columna y la desviación estándar.
  • Luego se toma el puntaje absoluto de Z-score porque la dirección no importa, solo si está por debajo del umbral.
  • all (axis = 1) garantiza que para cada fila, todas las columnas cumplan con la restricción.
  • Finalmente, el resultado de esta condición se utiliza para indexar el dataframe.

Use la indexación boolean como lo haría en numpy.array

 df = pd.DataFrame({'Data':np.random.normal(size=200)}) # example dataset of normally distributed data. df[np.abs(df.Data-df.Data.mean()) <= (3*df.Data.std())] # keep only the ones that are within +3 to -3 standard deviations in the column 'Data'. df[~(np.abs(df.Data-df.Data.mean()) > (3*df.Data.std()))] # or if you prefer the other way around 

Para una serie es similar:

 S = pd.Series(np.random.normal(size=200)) S[~((SS.mean()).abs() > 3*S.std())] 

Para cada una de las columnas de su dataframe, podría obtener cuantil con:

 q = df["col"].quantile(0.99) 

y luego filtrar con:

 df[df["col"] < q] 

Esta respuesta es similar a la proporcionada por @tanemaki, pero utiliza una expresión lambda lugar de scipy stats de scipy stats .

 df = pd.DataFrame(np.random.randn(100, 3), columns=list('ABC')) df[df.apply(lambda x: np.abs(x - x.mean()) / x.std() < 3).all(axis=1)] 

Para filtrar el dataframe donde solo UNA columna (por ejemplo, 'B') está dentro de tres desviaciones estándar:

 df[((df.B - df.B.mean()) / df.B.std()).abs() < 3] 
 #------------------------------------------------------------------------------ # accept a dataframe, remove outliers, return cleaned data in a new dataframe # see http://www.itl.nist.gov/div898/handbook/prc/section1/prc16.htm #------------------------------------------------------------------------------ def remove_outlier(df_in, col_name): q1 = df_in[col_name].quantile(0.25) q3 = df_in[col_name].quantile(0.75) iqr = q3-q1 #Interquartile range fence_low = q1-1.5*iqr fence_high = q3+1.5*iqr df_out = df_in.loc[(df_in[col_name] > fence_low) & (df_in[col_name] < fence_high)] return df_out 

Para cada serie en el dataframe, puede usar between y quantile para eliminar valores atípicos.

 x = pd.Series(np.random.normal(size=200)) # with outliers x = x[x.between(x.quantile(.25), x.quantile(.75))] # without outliers 

scipy.stats tiene los métodos trim1() y trimboth() para eliminar los valores atípicos en una sola fila, de acuerdo con la clasificación y un porcentaje introducido de valores eliminados.

Otra opción es transformar sus datos para mitigar el efecto de los valores atípicos. Puede hacer esto mediante la optimización de sus datos.

 import pandas as pd from scipy.stats import mstats %matplotlib inline test_data = pd.Series(range(30)) test_data.plot() 

Datos originales

 # Truncate values to the 5th and 95th percentiles transformed_test_data = pd.Series(mstats.winsorize(test_data, limits=[0.05, 0.05])) transformed_test_data.plot() 

Datos winsorized

Si le gusta el encadenamiento de métodos, puede obtener su condición booleana para todas las columnas numéricas como esta:

 df.sub(df.mean()).div(df.std()).abs().lt(3) 

Cada valor de cada columna se convertirá en True/False función de si sus menos de tres desviaciones estándar se alejan de la media o no.

A continuación se muestra un ejemplo completo con datos y 2 grupos:

Importaciones:

 from StringIO import StringIO import pandas as pd #pandas config pd.set_option('display.max_rows', 20) 

Ejemplo de datos con 2 grupos: G1: Grupo 1. G2: Grupo 2:

 TESTDATA = StringIO("""G1;G2;Value 1;A;1.6 1;A;5.1 1;A;7.1 1;A;8.1 1;B;21.1 1;B;22.1 1;B;24.1 1;B;30.6 2;A;40.6 2;A;51.1 2;A;52.1 2;A;60.6 2;B;80.1 2;B;70.6 2;B;90.6 2;B;85.1 """) 

Leer datos de texto en el dataframe de pandas:

 df = pd.read_csv(TESTDATA, sep=";") 

Definir los valores atípicos utilizando desviaciones estándar.

 stds = 1.0 outliers = df[['G1', 'G2', 'Value']].groupby(['G1','G2']).transform( lambda group: (group - group.mean()).abs().div(group.std())) > stds 

Defina valores de datos filtrados y los valores atípicos:

 dfv = df[outliers.Value == False] dfo = df[outliers.Value == True] 

Imprima el resultado:

 print '\n'*5, 'All values with decimal 1 are non-outliers. In the other hand, all values with 6 in the decimal are.' print '\nDef DATA:\n%s\n\nFiltred Values with %s stds:\n%s\n\nOutliers:\n%s' %(df, stds, dfv, dfo) 

Mi función para dejar atípicos

 def drop_outliers(df, field_name): distance = 1.5 * (np.percentile(df[field_name], 75) - np.percentile(df[field_name], 25)) df.drop(df[df[field_name] > distance + np.percentile(df[field_name], 75)].index, inplace=True) df.drop(df[df[field_name] < np.percentile(df[field_name], 25) - distance].index, inplace=True) 

Prefiero recortar en lugar de soltar. Lo siguiente se cortará en el lugar en el 2 y 98 pecentiles.

 df_list = list(df) minPercentile = 0.02 maxPercentile = 0.98 for _ in range(numCols): df[df_list[_]] = df[df_list[_]].clip((df[df_list[_]].quantile(minPercentile)),(df[df_list[_]].quantile(maxPercentile))) 

Ya que estoy en una etapa muy temprana de mi viaje hacia la ciencia de los datos, estoy tratando los valores atípicos con el código a continuación.

 #Outlier Treatment def outlier_detect(df): for i in df.describe().columns: Q1=df.describe().at['25%',i] Q3=df.describe().at['75%',i] IQR=Q3 - Q1 LTV=Q1 - 1.5 * IQR UTV=Q3 + 1.5 * IQR x=np.array(df[i]) p=[] for j in x: if j < LTV or j>UTV: p.append(df[i].median()) else: p.append(j) df[i]=p return df 

Suprimir estadísticamente el hecho de eliminar y eliminar los valores atípicos. Hace que los datos sean diferentes de los datos originales. También crea datos con formas desiguales y, por lo tanto, la mejor manera es reducir o evitar el efecto de los valores atípicos mediante la transformación de los datos en el registro. Esto funcionó para mí:

 np.log(data.iloc[:, :])