Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/319.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 大熊猫数据帧中分组剔除异常值的快速方法_Python_Pandas - Fatal编程技术网

Python 大熊猫数据帧中分组剔除异常值的快速方法

Python 大熊猫数据帧中分组剔除异常值的快速方法,python,pandas,Python,Pandas,我有一个相对较大的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.i

我有一个相对较大的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)
这是可行的,只是速度非常慢,可能是因为嵌套的
apply
调用:每个组一个,然后每个组中的每个列一个。我试图通过一次计算所有列的分位数来摆脱第二个
apply
,但在尝试用不同的值设置每个列的阈值时遇到了困难。有没有更快的方法来完成这个过程?

有一个你可以考虑使用的方法。但是请注意,它返回的值与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\u系列
可能(取决于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')


我发现了一种非常简单的方法来实现这一点,使用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)

实现这一点的好方法是矢量化。为此,我喜欢使用
np.where

import pandas as pd
import numpy as np
from scipy.stats import mstats
import timeit

data = pd.Series(range(20), dtype='float')

def WinsorizeCustom(data):
    quantiles = data.quantile([0.05, 0.95])
    q_05 = quantiles.loc[0.05]
    q_95 = quantiles.loc[0.95]

    out = np.where(data.values <= q_05,q_05, 
                                      np.where(data >= q_95, q_95, data)
                  )
    return out
但正如您所看到的,尽管我的函数非常快,但它离Scipy实现还很远:

%timeit WinsorizeCustom(data)
#1000 loops, best of 3: 842 µs per loop

%timeit WinsorizeStats(data)
#1000 loops, best of 3: 212 µs per loop

如果您有兴趣阅读更多有关加快pandas代码的内容,我建议您使用和。

以下是一个不使用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)

有一个二维数组,其中行作为观察值,列作为特征。 并且,要求省略具有任何异常特征值的完整行

data = np.array([[1, 8, 13, 113, 401],
                 [2, 8, 15, 119, 402],
                 [1, 9, 14, 117, 399],
                 [100, 7, 12, 110, 409],
                 [4, 70, 11, 111, 404]
                 ])

有任何API或函数可以这样做吗?

谢谢,这是一个很好的指针,我没有意识到scipy有一个
winsorize
函数。然而,我认为,如果有一种方法可以在数据帧上批量操作,而不必逐列操作,则可以实现更大的速度,类似于批量标准化或规范化的方法,例如,每个组中的日期数是否相同?按操作分组是按日期进行的,所以每个小组只有一个日期。你的意思是问每组的行数是否相同?答案是否定的,每个日期可以(而且通常是)有不同的行数。@YT正如你在OP中提到的,pandas现在有一个
.clip()
函数应该适合你,特别是当与
.quantile()
结合使用时。请看我刚刚发布的这个问题,然后用clip()和quantile()回答按照@Zhang18处理缺失值的建议:您可以添加一个简短的描述吗?
%timeit WinsorizeCustom(data)
#1000 loops, best of 3: 842 µs per loop

%timeit WinsorizeStats(data)
#1000 loops, best of 3: 212 µs per loop
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)
data = np.array([[1, 8, 13, 113, 401],
                 [2, 8, 15, 119, 402],
                 [1, 9, 14, 117, 399],
                 [100, 7, 12, 110, 409],
                 [4, 70, 11, 111, 404]
                 ])