Как удалить выброс из DataFrame с помощью IQR?

У меня есть Dataframe с большим количеством столбцов (около 100), я хочу применить метод межквартильного разметки и хотел удалить выброс из фрейма данных.

Я использую эту ссылку stackOverflow

Но проблема в том, что вышеприведенный метод работает правильно,

Как я пытаюсь вот так

Q1 = stepframe.quantile(0.25)
Q3 = stepframe.quantile(0.75)
IQR = Q3 - Q1
((stepframe < (Q1 - 1.5 * IQR)) | (stepframe > (Q3 + 1.5 * IQR))).sum()

это дает мне это

((stepframe < (Q1 - 1.5 * IQR)) | (stepframe > (Q3 + 1.5 * IQR))).sum()
Out[35]: 
Day                      0
Col1                     0
Col2                     0
col3                     0
Col4                     0
Step_Count            1179
dtype: int64

Я просто хотел знать, что я буду делать дальше, чтобы все выбросы из фрейма данных были удалены.

если я использую это

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

re_dat = remove_outlier(stepframe, stepframe.columns)

Я получаю эту ошибку

ValueError: Cannot index with multidimensional key

в этой строке

    df_out = df_in.loc[(df_in[col_name] > fence_low) & (df_in[col_name] < fence_high)]

3 ответа

Решение

Ты можешь использовать:

np.random.seed(33454)
stepframe = pd.DataFrame({'a': np.random.randint(1, 200, 20), 
                          'b': np.random.randint(1, 200, 20),
                          'c': np.random.randint(1, 200, 20)})

stepframe[stepframe > 150] *= 10
print (stepframe)

Q1 = stepframe.quantile(0.25)
Q3 = stepframe.quantile(0.75)
IQR = Q3 - Q1

df = stepframe[~((stepframe < (Q1 - 1.5 * IQR)) |(stepframe > (Q3 + 1.5 * IQR))).any(axis=1)]

print (df)
      a    b     c
1   109   50   124
3   137   60  1990
4    19  138   100
5    86   83   143
6    55   23    58
7    78  145    18
8   132   39    65
9    37  146  1970
13   67  148  1880
15  124  102    21
16   93   61    56
17   84   21    25
19   34   52   126

Детали:

Сначала создать boolean DataFrame с цепью |:

print (((stepframe < (Q1 - 1.5 * IQR)) | (stepframe > (Q3 + 1.5 * IQR))))
        a      b      c
0   False   True  False
1   False  False  False
2    True  False  False
3   False  False  False
4   False  False  False
5   False  False  False
6   False  False  False
7   False  False  False
8   False  False  False
9   False  False  False
10   True  False  False
11  False   True  False
12  False   True  False
13  False  False  False
14  False   True  False
15  False  False  False
16  False  False  False
17  False  False  False
18  False   True  False
19  False  False  False

А потом использовать DataFrame.any для проверки хотя бы одного True за строку и последнюю инвертированную логическую маску ~:

print (~((stepframe < (Q1 - 1.5 * IQR)) | (stepframe > (Q3 + 1.5 * IQR))).any(axis=1))
0     False
1      True
2     False
3      True
4      True
5      True
6      True
7      True
8      True
9      True
10    False
11    False
12    False
13     True
14    False
15     True
16     True
17     True
18    False
19     True
dtype: bool

invert решение с измененными условиями - < в >= а также > в <= по цепочке & для AND и последний фильтр по all для проверки всех True с на строки

print (((stepframe >= (Q1 - 1.5 * IQR)) & (stepframe <= (Q3 + 1.5 * IQR))).all(axis=1))
0     False
1      True
2     False
3      True
4      True
5      True
6      True
7      True
8      True
9      True
10    False
11    False
12    False
13     True
14    False
15     True
16     True
17     True
18    False
19     True
dtype: bool


df = stepframe[((stepframe >= (Q1 - 1.5 * IQR))& (stepframe <= (Q3 + 1.5 * IQR))).all(axis=1)]

Вы забыли написать название своей колонки в цитате (['col_name']).

Правильный:

df_out = df_in.loc[(df_in['col_name'] > fence_low) & (df_in['col_name'] < fence_high)]

Прежде чем использовать эту функцию, убедитесь, что ваша DATAFRAME находится в переменной «df», потому что я создаю эту функцию таким образом. Если у вас другое имя переменной DATAFRAME, замените «df» на имя переменной DATAFRAME и все.

Почему я создаю длинную функцию??-> Потому что мне нужна вся информация, такая как выбросы, индекс выбросов, чтобы я мог видеть, как она работает с нижним и верхним забором.

      def outliers(data):
    Q1 = data.quantile(0.25)
    Q3 = data.quantile(0.75)
    IQR = Q3 - Q1

    Lower_fence = Q1 - (1.5*IQR)
    print(f"Lower fence is = {Lower_fence}")

    Higher_fence = Q3 + (1.5*IQR)
    print(f"Higher fence is = {Higher_fence}")
      
    #here i'm taking all Outliers and appending this in Variable "Outlier".
    Outlier =[]
    for i in data:
        if i < Lower_fence:
            Outlier.append(i)
            data.drop(data==i)
        elif i > Higher_fence:
            Outlier.append(i)


    #With the help of "index" function here we are getting all the indexes of Lower_fence and Higher_fence

    Index_Outlier = df[data < Lower_fence ].index  
    Index_Outlier = df[data > Higher_fence].index


    #Here we are converting all the "Outliers" and "Index_Outliers" into string just to see all the data in One line
    #If you do print(Outliers) or print(Outliers_index) you will get every element of data in New Line.

    print(f"\nOutliers = {', '.join([str(x) for x in Outlier])}")
    print(f"\nOutliers_INDEX = {', '.join([str(x) for x in Index_Outlier ])}")

    #here we are seeing before and after shape.

    print(f'\nBEFORE dropping Outlier we have rows = {df.shape[0]}, and columns = {df.shape[1]}')

    df.drop(Index_Outlier,inplace=True)

    print(f'AFTER dropping Outlier we have rows = {df.shape[0]}, and columns = {df.shape[1]}')
Другие вопросы по тегам