我有一个很少列的熊猫数据帧。
现在我知道某些行是基于某个列值的异常值。
例如
列“Vol”的所有值都在12xx左右,其中一个值是4000(离群值)。
现在我想排除那些Vol列像这样的行。
所以,本质上,我需要在数据帧上放一个过滤器,这样我们就可以选择所有的行,其中某一列的值距离平均值在3个标准差之内。
实现这一点的优雅方式是什么?
我有一个很少列的熊猫数据帧。
现在我知道某些行是基于某个列值的异常值。
例如
列“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 ()