首页 > 解决方案 > Pandas Groupby 过滤器从每个组中删除异常值

问题描述

我有一个 Pandas DataFrame,其中包含 3 个分类分组变量和 1 个数值结果变量。在每个组内,有一个 n = 6,其中一个值可能是异常值(由每个组内的分布定义:异常值可以超过四分位数 3 的四分位数间距的 1.5 倍,或者小于四分位数 1 乘以四分位数间距的 1.5 倍)。

DataFrame 的示例如下所示:

# Making the df without our outcome variable

import numpy as np
import pandas as pd

G1 = np.repeat(['E', 'F'], 24)
G2 = np.tile(np.repeat(['C', 'D'], 6), 4)
G3 = np.tile(np.repeat(['A', 'B'], 12), 2)

dummy_data = pd.DataFrame({'G1' : G1, 'G2' : G2, 'G3': G3})

# Defining a function to generate a numpy array with n = 6, where one of these values is an outlier # by our previous definition

np.random.seed(0)

def outlier_arr(low, high):
    norm_arr = np.random.randint(low, high, 5)

    IQR = np.percentile(norm_arr, 75) - np.percentile(norm_arr, 25)
    upper_fence = np.percentile(norm_arr, 75) + (IQR * 1.5)
    lower_fence = np.percentile(norm_arr, 25) - (IQR * 1.5)
    rand_decision = np.random.randint(0, 2, 1)[0]

    if rand_decision == 1:
        high_outlier = np.round(upper_fence * 3, decimals = 0)
        final_arr = np.hstack([norm_arr, high_outlier])

    else:
        low_outlier = np.round(lower_fence * (1/3), decimals = 0)
        final_arr = np.hstack([norm_arr, low_outlier])

    return final_arr.astype(int)

# Making a list to add into the dataframe to represent our values

abund_arr = []

for i in range(0, 8):
    abund_arr = abund_arr + outlier_arr(700, 800).tolist()

abund_arr = np.array(abund_arr)

# Appending this list as a new row

dummy_data['V1'] = abund_arr

这应该生成一个包含 3 个分组变量G1G2G3以及一个结果变量的 DataFrame,V1其中每个组都应该有一个需要删除的异常值。我们可以查看下面的前 6 行(单个组),dummy_data.head(6)看看其中一个值(最后一行)是我们想要过滤掉的异常值。


    G1  G2  G3  V1
0   E   C   A   744
1   E   C   A   747
2   E   C   A   764
3   E   C   A   767
4   E   C   A   767
5   E   C   A   2391 <--- outlier

据我了解,一个好的方法可能是使用 df.groupby().filter(),并按变量分组G1G2G3实现一个用户定义的函数,filter()该函数根据上面讨论的异常值标准返回 T/F .

我已经尝试过,其中用于检测数组中异常值(返回Trueor数组False)的函数如下所示:

def is_outlier(x): 

    IQR = np.percentile(x, 75) - np.percentile(x, 25)
    upper_fence = np.percentile(x, 75) + (IQR * 1.5)
    lower_fence = np.percentile(x, 25) - (IQR * 1.5)

    return (x > upper_fence) | (x < lower_fence)

正确检测到异常值,如下所示:

test_arr = outlier_arr(300, 500)

is_outlier(test_arr)

# returns an array of [False, False, False, False, False,  True]

但是,当在 pandas 对象上使用上述方法时,以下代码不会引发错误,但也不会过滤任何异常值:

dummy_data.groupby(['G1', 'G2', 'G3']).filter(lambda x: (is_outlier(x['V1'])).any())

注意:我实际上在这里找到了一种方法,在这里你使用apply()而不是filter().

运行dummy_data[~dummy_data.groupby(['G1', 'G2', 'G3'])['V1'].apply(is_outlier)]产生了预期的结果。

但是,只是为了使用这种方法,需要进行什么调整才能使其正常工作filter()?如果可能,这两种方法中哪一种是正确/首选的?

提前致谢。

标签: pythonarrayspandasnumpydataframe

解决方案


推荐阅读