Как использовать фильтр панд с IQR?

Существует ли встроенный способ фильтрации столбца по IQR(т. Е. Значения между Q1-1.5IQR и Q3+1.5IQR)? также будет приветствоваться любая другая возможная обобщенная фильтрация в предложенных пандах.

3 ответа

Насколько я знаю, наиболее компактные обозначения, кажется, принесены 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)')

Затем мы можем построить результат, чтобы проверить разницу. Мы видим, что выброс в левом блокпосте (крест на 183) больше не появляется в отфильтрованной серии.

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

Сравнение до и после фильтра

После этого ответа я написал пост на эту тему, где вы можете найти больше информации.

Другой подход с использованием Series.between():

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

Вычеркнуто:

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

Это даст вам подмножество df который лежит в IQR столбца column:

def get_subset_by_IQR(df,column):
    q1 = df[column].quantile(0.25)
    q3 = df[column].quantile(0.75)
    iqr = (df[column] >= q1) & (df[column] <= q3)
    return df.loc[iqr]

Найдите 1-й и 3-й квартили, используя df.quantileа затем используйте маску на фрейме данных. Если вы хотите их удалить, используйте no_outliers и инвертируем условие в маске, чтобы получить outliers.

Q1 = df.col.quantile(0.25)
Q3 = df.col.quantile(0.75)
IQR = Q3 - Q1
no_outliers = df.col[(Q1 - 1.5*IQR < df.BMI) &  (df.BMI < Q3 + 1.5*IQR)]
outliers = df.col[(Q1 - 1.5*IQR >= df.BMI) |  (df.BMI >= Q3 + 1.5*IQR)]

Другой подход использует Series.clip:

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

вот подробности:

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

Использование его для фильтрации целого кадра данных df это просто:

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

Примечание: метод исключает сами границы.

Вы также можете попробовать использовать приведенный ниже код, рассчитав IQR. На основании IQR, нижней и верхней границ, он заменит значение выбросов, представленных в каждом столбце. этот код будет проходить через все столбцы в фрейме данных и работать по одному, отфильтровывая только выбросы, вместо того, чтобы проходить все значения в строках для поиска выбросов.

Функция:

    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)

Вызов функции:

df = mod_outlier(df)
Другие вопросы по тегам