我有一个很少列的熊猫数据帧。

现在我知道某些行是基于某个列值的异常值。

例如

列“Vol”的所有值都在12xx左右,其中一个值是4000(离群值)。

现在我想排除那些Vol列像这样的行。

所以,本质上,我需要在数据帧上放一个过滤器,这样我们就可以选择所有的行,其中某一列的值距离平均值在3个标准差之内。

实现这一点的优雅方式是什么?


当前回答

另一种选择是转换数据,以减轻异常值的影响。你可以通过winsorize你的数据来做到这一点。

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

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

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

其他回答

如果你喜欢方法链接,你可以得到所有数值列的布尔条件,如下所示:

df.sub(df.mean()).div(df.std()).abs().lt(3)

每一列的每个值都将根据其是否距离平均值小于三个标准差而转换为True/False。

你可以使用布尔掩码:

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

由于我还没有看到处理数值和非数值属性的答案,这里有一个补充答案。

您可能只希望删除数值属性上的异常值(类别变量几乎不可能是异常值)。

函数定义

我扩展了@tanemaki的建议,当非数值属性也存在时处理数据:

from scipy import stats

def drop_numerical_outliers(df, z_thresh=3):
    # Constrains will contain `True` or `False` depending on if it is a value below the threshold.
    constrains = df.select_dtypes(include=[np.number]) \
        .apply(lambda x: np.abs(stats.zscore(x)) < z_thresh, reduce=False) \
        .all(axis=1)
    # Drop (inplace) values set to be rejected
    df.drop(df.index[~constrains], inplace=True)

使用

drop_numerical_outliers(df)

例子

想象一个数据集df,其中包含一些关于房屋的值:小巷、土地轮廓、销售价格……例:数据文档

首先,你想要在散点图上可视化数据(z-score Thresh=3):

# Plot data before dropping those greater than z-score 3. 
# The scatterAreaVsPrice function's definition has been removed for readability's sake.
scatterAreaVsPrice(df)

# Drop the outliers on every attributes
drop_numerical_outliers(train_df)

# Plot the result. All outliers were dropped. Note that the red points are not
# the same outliers from the first plot, but the new computed outliers based on the new data-frame.
scatterAreaVsPrice(train_df)

把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

去掉离群值的函数

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)