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

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

例如

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

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

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

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


当前回答

删除至少一列中有异常值的所有行

如果你的数据框架中有多列,并且想要删除至少一列中有异常值的所有行,下面的表达式可以一次性完成:

import pandas as pd
import numpy as np
from scipy import stats


df = pd.DataFrame(np.random.randn(100, 3))

df[(np.abs(stats.zscore(df)) < 3).any(axis=1)]

描述:

中的每个列,它首先计算每个值的z分数 列,相对于列的均值和标准差。 然后取绝对z分数,因为方向没有 物质,只有当它低于阈值时。 All(轴=1)确保对于每一行,所有列都满足 约束。 最后,这个条件的结果被用于索引数据帧。

基于单个列筛选其他列

为zscore指定一列,例如df[0],并删除.all(axis=1)。

df[(np.abs(stats.zscore(df[0])) < 3)]

其他回答

scipy。Stats有方法trim1()和trimboth(),根据排名和被删除值的引入百分比,在单行中删除异常值。

把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

像在numpy.array中那样使用布尔索引

df = pd.DataFrame({'Data':np.random.normal(size=200)})
# example dataset of normally distributed data. 

df[np.abs(df.Data-df.Data.mean()) <= (3*df.Data.std())]
# keep only the ones that are within +3 to -3 standard deviations in the column 'Data'.

df[~(np.abs(df.Data-df.Data.mean()) > (3*df.Data.std()))]
# or if you prefer the other way around

对于一个系列,它是类似的:

S = pd.Series(np.random.normal(size=200))
S[~((S-S.mean()).abs() > 3*S.std())]

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

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

函数定义

我扩展了@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)

在回答实际问题之前,我们应该问另一个非常相关的问题,这取决于你的数据的性质:

什么是异常值?

想象一下数值[3,2,3,4,999]的序列(其中999似乎不适合),并分析各种异常值检测方法

z分数

这里的问题是,所讨论的值严重扭曲了我们的测量均值和std,导致不明显的z分数大约为[-0.5,-0.5,-0.5,-0.5,2.0],使每个值保持在均值的两个标准偏差内。因此,一个非常大的离群值可能会扭曲你对离群值的整个评估。我反对这种方法。

分位数过滤器

给出的一种更健壮的方法是这样的答案,消除了底部和顶部1%的数据。然而,如果这些数据真的是异常值,这就排除了一个与问题无关的固定分数。您可能会丢失大量有效数据,另一方面,如果您有超过1%或2%的数据作为异常值,则仍然会保留一些异常值。

距中位数的距离

更健壮的分位数原则:删除所有距离数据中位数超过f倍四分位数范围的数据。这也是sklearn的RobustScaler所使用的转换。IQR和中位数对异常值具有鲁棒性,因此您可以聪明地解决z分数方法的问题。

在正态分布中,我们大致有iqr=1.35*s,所以你可以将z-score过滤器的z=3转换为iqr过滤器的f=2.22。这将在上面的例子中删除999。

基本假设是,至少数据的“中间一半”是有效的,并且与分布很相似,然而,如果分布尾部较宽,q_25%到q_75%的区间较窄,那么也会搞砸。

高级统计方法

当然,也有一些漂亮的数学方法,如Peirce准则,Grubb的检验或Dixon的q检验,只是举几个也适用于非正态分布数据的例子。它们都不容易实现,因此没有进一步解决。

Code

用np替换所有数值列的所有异常值。Nan在一个例子数据帧上。该方法对于pandas提供的所有dtype都是健壮的,并且可以很容易地应用于混合类型的数据帧:

import pandas as pd
import numpy as np                                     

# sample data of all dtypes in pandas (column 'a' has an outlier)         # dtype:
df = pd.DataFrame({'a': list(np.random.rand(8)) + [123456, np.nan],       # float64
                   'b': [0,1,2,3,np.nan,5,6,np.nan,8,9],                  # int64
                   'c': [np.nan] + list("qwertzuio"),                     # object
                   'd': [pd.to_datetime(_) for _ in range(10)],           # datetime64[ns]
                   'e': [pd.Timedelta(_) for _ in range(10)],             # timedelta[ns]
                   'f': [True] * 5 + [False] * 5,                         # bool
                   'g': pd.Series(list("abcbabbcaa"), dtype="category")}) # category
cols = df.select_dtypes('number').columns  # limits to a (float), b (int) and e (timedelta)
df_sub = df.loc[:, cols]


# OPTION 1: z-score filter: z-score < 3
lim = np.abs((df_sub - df_sub.mean()) / df_sub.std(ddof=0)) < 3

# OPTION 2: quantile filter: discard 1% upper / lower values
lim = np.logical_and(df_sub < df_sub.quantile(0.99, numeric_only=False),
                     df_sub > df_sub.quantile(0.01, numeric_only=False))

# OPTION 3: iqr filter: within 2.22 IQR (equiv. to z-score < 3)
iqr = df_sub.quantile(0.75, numeric_only=False) - df_sub.quantile(0.25, numeric_only=False)
lim = np.abs((df_sub - df_sub.median()) / iqr) < 2.22


# replace outliers with nan
df.loc[:, cols] = df_sub.where(lim, np.nan)

删除包含至少一个nan-value的所有行:

df.dropna(subset=cols, inplace=True) # drop rows with NaN in numerical columns
# or
df.dropna(inplace=True)  # drop rows with NaN in any column

使用pandas 1.3函数:

pandas.DataFrame.select_dtypes () pandas.DataFrame.quantile () pandas.DataFrame.where () pandas.DataFrame.dropna ()