我有一个很少列的熊猫数据帧。
现在我知道某些行是基于某个列值的异常值。
例如
列“Vol”的所有值都在12xx左右,其中一个值是4000(离群值)。
现在我想排除那些Vol列像这样的行。
所以,本质上,我需要在数据帧上放一个过滤器,这样我们就可以选择所有的行,其中某一列的值距离平均值在3个标准差之内。
实现这一点的优雅方式是什么?
我有一个很少列的熊猫数据帧。
现在我知道某些行是基于某个列值的异常值。
例如
列“Vol”的所有值都在12xx左右,其中一个值是4000(离群值)。
现在我想排除那些Vol列像这样的行。
所以,本质上,我需要在数据帧上放一个过滤器,这样我们就可以选择所有的行,其中某一列的值距离平均值在3个标准差之内。
实现这一点的优雅方式是什么?
当前回答
下面是一个包含数据和2组的完整示例:
进口:
from StringIO import StringIO
import pandas as pd
#pandas config
pd.set_option('display.max_rows', 20)
有2个组的数据示例:G1:Group 1。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)
其他回答
由于我还没有看到处理数值和非数值属性的答案,这里有一个补充答案。
您可能只希望删除数值属性上的异常值(类别变量几乎不可能是异常值)。
函数定义
我扩展了@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)
下面是一个包含数据和2组的完整示例:
进口:
from StringIO import StringIO
import pandas as pd
#pandas config
pd.set_option('display.max_rows', 20)
有2个组的数据示例:G1:Group 1。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)
这个答案类似于@tanemaki提供的答案,但使用了lambda表达式而不是scipy stats。
df = pd.DataFrame(np.random.randn(100, 3), columns=list('ABC'))
standard_deviations = 3
df[df.apply(lambda x: np.abs(x - x.mean()) / x.std() < standard_deviations)
.all(axis=1)]
要过滤只有一个列的数据帧(例如:B)在三个标准差之内:
df[((df['B'] - df['B'].mean()) / df['B'].std()).abs() < standard_deviations]
关于如何在滚动的基础上应用这个z-score:滚动z-score应用于pandas数据框架
#------------------------------------------------------------------------------
# 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
scipy。Stats有方法trim1()和trimboth(),根据排名和被删除值的引入百分比,在单行中删除异常值。