小智 173
如果数据框中有多个列,并且想要删除至少有一列中具有异常值的所有行,则以下表达式将一次性执行此操作.
df = pd.DataFrame(np.random.randn(100, 3))
from scipy import stats
df[(np.abs(stats.zscore(df)) < 3).all(axis=1)]
Run Code Online (Sandbox Code Playgroud)
描述:
CT *_*Zhu 131
boolean您可以像使用索引一样使用索引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
Run Code Online (Sandbox Code Playgroud)
对于一个系列,它是相似的:
S = pd.Series(np.random.normal(size=200))
S[~((S-S.mean()).abs() > 3*S.std())]
Run Code Online (Sandbox Code Playgroud)
use*_*745 75
对于每个dataframe列,您可以获得分位数:
q = df["col"].quantile(0.99)
Run Code Online (Sandbox Code Playgroud)
然后过滤:
df[df["col"] < q]
Run Code Online (Sandbox Code Playgroud)
asc*_*ter 54
在回答实际问题之前,我们应该问另一个非常相关的问题,具体取决于您的数据的性质:
想象一系列值[3, 2, 3, 4, 999](看似999不适合的值)并分析异常值检测的各种方法
这里的问题是,所讨论的值严重扭曲了我们的测量mean,std导致不明显的 z 分数大致为[-0.5, -0.5, -0.5, -0.5, 2.0],将每个值保持在平均值的两个标准差之内。因此,一个非常大的异常值可能会扭曲您对异常值的整体评估。我不鼓励这种方法。
这个答案给出了一种更稳健的方法,消除了底部和顶部 1% 的数据。然而,这消除了与这些数据是否确实异常值无关的问题的固定分数。您可能会丢失大量有效数据,但另一方面,如果您的数据中有超过 1% 或 2% 的异常值,则仍然会保留一些异常值。
分位数原则的更强大版本:消除所有距数据中位数超过四分位数范围f倍的数据。这也是例如使用的转换。IQR 和中位数对于异常值具有鲁棒性,因此您可以智取 z 分数方法的问题。sklearnRobustScaler
在正态分布中,我们大致有iqr=1.35*s,因此您可以z=3将 z 分数过滤器转换为f=2.22iqr 过滤器。这将删除999上面示例中的 。
基本假设是,至少数据的“中间一半”是有效的,并且与分布非常相似,而如果您的分布具有宽尾部和狭窄的 q_25% 到 q_75% 区间,那么您也会搞砸。
当然,还有一些奇特的数学方法,例如Peirce 准则、Grubb 检验或Dixon 检验,仅举几个也适用于非正态分布数据的方法。它们都不容易实现,因此没有进一步解决。
np.nan使用示例数据框替换所有数字列的所有异常值。该方法对于pandas 提供的所有数据类型都具有鲁棒性,并且可以轻松应用于混合类型的数据框:
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)
Run Code Online (Sandbox Code Playgroud)
要删除至少包含一个 nan 值的所有行:
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
Run Code Online (Sandbox Code Playgroud)
使用 pandas 1.3 函数:
pandas.DataFrame.select_dtypes()pandas.DataFrame.quantile()pandas.DataFrame.where()pandas.DataFrame.dropna()Ale*_*der 27
这个答案类似于@tanemaki提供的答案,但使用的是lambda表达而不是scipy stats.
df = pd.DataFrame(np.random.randn(100, 3), columns=list('ABC'))
df[df.apply(lambda x: np.abs(x - x.mean()) / x.std() < 3).all(axis=1)]
Run Code Online (Sandbox Code Playgroud)
要过滤DataFrame,其中只有一列(例如'B')在三个标准差内:
df[((df.B - df.B.mean()) / df.B.std()).abs() < 3]
Run Code Online (Sandbox Code Playgroud)
小智 20
#------------------------------------------------------------------------------
# accept a dataframe, remove outliers, return cleaned data in a new dataframe
# see http://www.itl.nist.gov/div898/handbook/prc/section1/prc16.htm
#------------------------------------------------------------------------------
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
Run Code Online (Sandbox Code Playgroud)
Jef*_*dez 16
对于数据框中的每个系列,您可以使用between和quantile删除异常值.
x = pd.Series(np.random.normal(size=200)) # with outliers
x = x[x.between(x.quantile(.25), x.quantile(.75))] # without outliers
Run Code Online (Sandbox Code Playgroud)
Key*_*r00 10
由于我还没有看到涉及数字和非数字属性的答案,因此这里是一个补充答案。
您可能只想将离群值放在数字属性上(分类变量几乎不可能是离群值)。
功能定义
我扩展了@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)
Run Code Online (Sandbox Code Playgroud)
用法
drop_numerical_outliers(df)
Run Code Online (Sandbox Code Playgroud)
例
想象一个数据集df,其中包含有关房屋的一些值:胡同,土地轮廓,售价,...例如:数据文档
首先,您要可视化散点图上的数据(z分数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)
Run Code Online (Sandbox Code Playgroud)
# 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)
Run Code Online (Sandbox Code Playgroud)
如果你喜欢方法链,你可以得到所有数字列的布尔条件,如下所示:
df.sub(df.mean()).div(df.std()).abs().lt(3)
Run Code Online (Sandbox Code Playgroud)
每列的每个值将True/False根据其与平均值的距离是否小于三个标准差来转换。
另一种选择是转换您的数据,以减轻异常值的影响。您可以通过对数据进行 Winsorizing 来做到这一点。
import pandas as pd
from scipy.stats import mstats
%matplotlib inline
test_data = pd.Series(range(30))
test_data.plot()
Run Code Online (Sandbox Code Playgroud)
# 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()
Run Code Online (Sandbox Code Playgroud)
您可以使用布尔掩码:
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])
Run Code Online (Sandbox Code Playgroud)
输出:
train y
2 2 0
3 3 1
4 4 1
5 5 0
6 6 0
7 7 1
8 8 1
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
192212 次 |
| 最近记录: |