我有一个相对较大的DataFrame对象(大约一百万行,几百列),并且我想逐组裁剪每列中的离群值。我的意思是“逐组剪裁每列的异常值”-计算组中每列的5%和95%分位数,并剪裁该分位数范围之外的值。
下面是我目前正在使用的设置:
def winsorize_series(s):
q = s.quantile([0.05, 0.95])
if isinstance(q, pd.Series) and len(q) == 2:
s[s < q.iloc[0]] = q.iloc[0]
s[s > q.iloc[1]] = q.iloc[1]
return s
def winsorize_df(df):
return df.apply(winsorize_series, axis=0)
然后,使用名为features
并由DATE
编制索引的DataFrame,我可以执行以下操作
grouped = features.groupby(level='DATE')
result = grouped.apply(winsorize_df)
这是可行的,除非它非常慢,这可能是由于嵌套的apply
调用:每组一个,然后每组中的每列一个。我试图通过一次计算所有列的分位数来消除第二个apply
,但在尝试通过不同的值设置每列的阈值时遇到了问题。有没有更快的方法来完成这个过程?
发布于 2014-12-11 23:13:04
有一个你可以考虑使用的winsorize function in scipy.stats.mstats。但是请注意,它返回的值与winsorize_series
略有不同
In [126]: winsorize_series(pd.Series(range(20), dtype='float'))[0]
Out[126]: 0.95000000000000007
In [127]: mstats.winsorize(pd.Series(range(20), dtype='float'), limits=[0.05, 0.05])[0]
Out[127]: 1.0
使用mstats.winsorize
而不是winsorize_series
可能(取决于N,M,P)大约快1.5倍:
import numpy as np
import pandas as pd
from scipy.stats import mstats
def using_mstats_df(df):
return df.apply(using_mstats, axis=0)
def using_mstats(s):
return mstats.winsorize(s, limits=[0.05, 0.05])
N, M, P = 10**5, 10, 10**2
dates = pd.date_range('2001-01-01', periods=N//P, freq='D').repeat(P)
df = pd.DataFrame(np.random.random((N, M))
, index=dates)
df.index.names = ['DATE']
grouped = df.groupby(level='DATE')
In [122]: %timeit result = grouped.apply(winsorize_df)
1 loops, best of 3: 17.8 s per loop
In [123]: %timeit mstats_result = grouped.apply(using_mstats_df)
1 loops, best of 3: 11.2 s per loop
发布于 2019-04-16 18:55:08
这是一个不使用scipy.stats.mstats的解决方案:
def clip_series(s, lower, upper):
clipped = s.clip(lower=s.quantile(lower), upper=s.quantile(upper), axis=1)
return clipped
# Manage list of features to be winsorized
feature_list = list(features.columns)
for f in feature_list:
features[f] = clip_series(features[f], 0.05, 0.95)
发布于 2017-09-07 01:05:40
我找到了一种相当简单的方法来实现这一点,在pandas中使用transform方法。
from scipy.stats import mstats
def winsorize_series(group):
return mstats.winsorize(group, limits=[lower_lim,upper_lim])
grouped = features.groupby(level='DATE')
result = grouped.transform(winsorize_series)
https://stackoverflow.com/questions/27424178
复制相似问题