web-dev-qa-db-fra.com

comment utiliser pandas filtre avec IQR?

Existe-t-il un moyen intégré de filtrer une colonne par IQR (c'est-à-dire des valeurs comprises entre Q1-1.5IQR et Q3 + 1.5IQR)? également, tout autre filtrage généralisé possible dans pandas suggéré sera apprécié.

23
Qijun Liu

Pour autant que je sache, la notation la plus compacte semble être apportée par la méthode query.

# Some test data
np.random.seed(33454)
df = (
    # A standard distribution
    pd.DataFrame({'nb': np.random.randint(0, 100, 20)})
        # Adding some outliers
        .append(pd.DataFrame({'nb': np.random.randint(100, 200, 2)}))
        # Reseting the index
        .reset_index(drop=True)
    )

# Computing IQR
Q1 = df['nb'].quantile(0.25)
Q3 = df['nb'].quantile(0.75)
IQR = Q3 - Q1

# Filtering Values between Q1-1.5IQR and Q3+1.5IQR
filtered = df.query('(@Q1 - 1.5 * @IQR) <= nb <= (@Q3 + 1.5 * @IQR)')

Ensuite, nous pouvons tracer le résultat pour vérifier la différence. On observe que la valeur aberrante dans le boxplot de gauche (la croix à 183) n'apparaît plus dans la série filtrée.

# Ploting the result to check the difference
df.join(filtered, rsuffix='_filtered').boxplot()

Comparison before and after filterinf

Depuis cette réponse, j'ai écrit un post sur ce sujet où vous pouvez trouver plus d'informations.

31
Romain

Une autre approche utilisant Series.between ():

iqr = df['col'][df['col'].between(df['col'].quantile(.25), df['col'].quantile(.75), inclusive=True)]

Extrait:

q1 = df['col'].quantile(.25)
q3 = df['col'].quantile(.75)
mask = d['col'].between(q1, q2, inclusive=True)
iqr = d.loc[mask, 'col']
9
MLenthousiast

Cela vous donnera le sous-ensemble de df qui se trouve dans l'IQR de la colonne column:

def subset_by_iqr(df, column, whisker_width=1.5):
    """Remove outliers from a dataframe by column, including optional 
       whiskers, removing rows for which the column value are 
       less than Q1-1.5IQR or greater than Q3+1.5IQR.
    Args:
        df (`:obj:pd.DataFrame`): A pandas dataframe to subset
        column (str): Name of the column to calculate the subset from.
        whisker_width (float): Optional, loosen the IQR filter by a
                               factor of `whisker_width` * IQR.
    Returns:
        (`:obj:pd.DataFrame`): Filtered dataframe
    """
    # Calculate Q1, Q2 and IQR
    q1 = df[column].quantile(0.25)                 
    q3 = df[column].quantile(0.75)
    iqr = q3 - q1
    # Apply filter with respect to IQR, including optional whiskers
    filter = (df[column] >= q1 - whisker_width*iqr) & (df[column] <= q3 + whisker_width*iqr)
    return df.loc[filter]                                                     

# Example for whiskers = 1.5, as requested by the OP
df_filtered = subset_by_iqr(df, 'column_name', whisker_width=1.5)
6
kd88

Vous pouvez également utiliser le code ci-dessous en calculant l'IQR. Sur la base de l'IQR, bornes inférieure et supérieure, il remplacera la valeur des valeurs aberrantes présentée dans chaque colonne. ce code passera par chaque colonne dans la trame de données et fonctionnera une par une en filtrant les valeurs aberrantes seules, au lieu de parcourir toutes les valeurs des lignes pour trouver les valeurs aberrantes.

Une fonction:

    def mod_outlier(df):
        df1 = df.copy()
        df = df._get_numeric_data()


        q1 = df.quantile(0.25)
        q3 = df.quantile(0.75)

        iqr = q3 - q1

        lower_bound = q1 -(1.5 * iqr) 
        upper_bound = q3 +(1.5 * iqr)


        for col in col_vals:
            for i in range(0,len(df[col])):
                if df[col][i] < lower_bound[col]:            
                    df[col][i] = lower_bound[col]

                if df[col][i] > upper_bound[col]:            
                    df[col][i] = upper_bound[col]    


        for col in col_vals:
            df1[col] = df[col]

        return(df1)

Appel de fonction:

df = mod_outlier(df)
0
Ayyasamy

Une autre approche utilise Series.clip:

q = s.quantile([.25, .75])
s = s[~s.clip(*q).isin(q)]

voici les détails:

s = pd.Series(np.randon.randn(100))
q = s.quantile([.25, .75])  # calculate lower and upper bounds
s = s.clip(*q)  # assigns values outside boundary to boundary values
s = s[~s.isin(q)]  # take only observations within bounds

Son utilisation pour filtrer une trame de données entière df est simple:

def iqr(df, colname, bounds = [.25, .75]):
    s = df[colname]
    q = s.quantile(bounds)
    return df[~s.clip(*q).isin(q)]

Remarque: la méthode exclut les limites elles-mêmes.

0
danek