在pandas DataFrame中检测和排除异常值

389
我有一个带有几列的pandas数据框。 现在我知道某些行是基于某列值的异常值。 例如 列Vol的所有值都在12xx左右,而一个值是4000(异常值)。 我想要排除那些具有这样的Vol列的行。
因此,本质上我需要在数据框上设置一个过滤器,以便选择所有某列值在平均值的3个标准差范围内的行。
有什么优雅的方法可以实现这个?
19个回答

9

另一个选择是转换数据,以减轻离群值的影响。您可以通过winsorizing数据来实现这一点。

import pandas as pd
from scipy.stats import mstats
%matplotlib inline

test_data = pd.Series(range(30))
test_data.plot()

Original data

# 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()

Winsorized data


7

您可以使用布尔掩码:

import pandas as pd

def remove_outliers(df, q=0.05):
    upper = df.quantile(1-q)
    lower = df.quantile(q)
    mask = (df < upper) & (df > lower)
    return mask

t = pd.DataFrame({'train': [1,1,2,3,4,5,6,7,8,9,9],
                  'y': [1,0,0,1,1,0,0,1,1,1,0]})

mask = remove_outliers(t['train'], 0.1)

print(t[mask])

输出:

   train  y
2      2  0
3      3  1
4      4  1
5      5  0
6      6  0
7      7  1
8      8  1

3

因为我在数据科学的早期阶段,所以我使用以下代码来处理异常值。

#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

3

将98分位数和2分位数作为离群值的限制

upper_limit = np.percentile(X_train.logerror.values, 98) 
lower_limit = np.percentile(X_train.logerror.values, 2) # Filter the outliers from the dataframe
data[‘target’].loc[X_train[‘target’]>upper_limit] = upper_limit data[‘target’].loc[X_train[‘target’]<lower_limit] = lower_limit

2

我更喜欢剪裁而不是删除。以下内容将在第2个和第98个百分位数处进行剪裁。

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)))

2

去除异常值的函数

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)

2
一份包含数据和两个分组的完整示例如下:
导入:

Imports:

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

具有2个组的数据示例:G1:第一组。G2:第二组:

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
""")

将文本数据读入Pandas数据框:

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

使用标准差定义异常值

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

定义筛选数据值和异常值:

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

打印结果:

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)

0
如果您的数据框中有异常值,有许多方法可以处理这些异常值:
其中大部分在我的文章中提到:请阅读此文 在此处查找代码:笔记本

虽然这个链接可能回答了问题,但最好在此处包含答案的基本部分并提供参考链接。如果链接页面更改,仅有链接的答案可能会失效。- 来自审查 - Plagon

-4

我认为在统计学上删除和丢弃异常值是错误的做法。这会使数据与原始数据不同,也会使数据形状不均匀,因此最好的方法是通过对数据进行对数转换来减少或避免异常值的影响。这对我很有效:

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

5
不能假设为什么原帖作者想要做某件事情。 - RajeshM

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接