【问题标题】:Winsorize dataframe columns per month while ignoring NaN's每月对数据框列进行 Winsorize,同时忽略 NaN
【发布时间】:2021-04-17 19:17:55
【问题描述】:

我有一个包含每月数据和以下列的数据框:日期、bm 和现金

date        bm        cash
1981-09-30  0.210308  2.487146
1981-10-31  0.241291  2.897529
1981-11-30  0.221529  2.892758
1981-12-31  0.239002  2.726372
1981-09-30  0.834520  4.387087
1981-10-31  0.800472  4.297658
1981-11-30  0.815778  4.459382
1981-12-31  0.836681  4.895269

现在我想每月对我的数据进行缩尾处理,同时在数据中保留 NaN 值。 IE。我想每月对数据进行分组,并分别用 99 个百分位和 0.01 个百分位覆盖 0.99 以上和 0.01 个百分位以下的观察值。从Winsorizing data by column in pandas with NaN 我发现我应该用“剪辑”功能来做这件事。我的代码如下所示:

df['date'] = pd.to_datetime(df['date'])
df = df.set_index(['date'])
df_grouped = df.groupby(pd.Grouper(freq='M'))
cols = df.columns
for c in cols:
    df[c] = df_grouped[c].apply(lambda x: x.clip(lower=x.quantile(0.01), upper=x.quantile(0.99)))

我得到以下输出:ValueError: cannot reindex from a duplicate axis

附:我意识到我没有包括我所需的输出,但我希望所需的输出是明确的。否则我可以尝试把一些东西放在一起。

编辑:来自@Allolz 的这些解决方案已经提供了很大帮助,但它并不能完全按预期工作。在我从@Allolz 运行代码之前,我运行了: df_in.groupby(pd.Grouper(freq='M', key='date'))['secured'].quantile([0, 0.01, 0.25, 0.5, 0.75, 0.99, 1])

返回:

date            
1980-01-31  0.00    1.580564e+00
            0.01    1.599805e+00
            0.25    2.388106e+00
            0.50    6.427071e+00
            0.75    1.200685e+01
            0.99    5.133111e+01
            1.00    5.530329e+01

winsorizing 后我得到:

date            
1980-01-31  0.00         1.599805
            0.01         1.617123
            0.25         2.388106
            0.50         6.427071
            0.75        12.006854
            0.99        47.756152
            1.00        51.331114

很明显,新的 0.0 和 1.0 分位数等于原来的 0.01 和 0.09 分位数,这是我们所期望的。但是,新的 0.01 和 0.99 分位数不等于原来的 0.01 和 0.99 分位数,我希望它们应该保持不变。什么会导致这种情况,而 wat 可以解决它吗?我的直觉是它可能与数据中的 NaN 有关,但我不确定这是否真的是原因。

【问题讨论】:

    标签: python pandas nan clip


    【解决方案1】:

    一种更快的方法要求您创建辅助列。我们将使用groupby + transform 将 0.01 和 0.99 分位数(对于该月份组)的列广播回 DataFrame,然后您可以使用这些 Series 一次剪辑原始数据。 (clip 将单独留下NaN,因此它也满足该要求)。然后,如果您愿意,请删除辅助列(为了清楚起见,我将保留它们)。

    样本数据

    import numpy as np
    import panda as pd
    
    np.random.seed(123)
    N = 10000
    df = pd.DataFrame({'date': np.random.choice(pd.date_range('2010-01-01', freq='MS', periods=12), N),
                       'val': np.random.normal(1, 0.95, N)})
    

    代码

    gp = df.groupby(pd.Grouper(freq='M', key='date'))['val']
    
    # Assign the lower-bound ('lb') and upper-bound ('ub') for Winsorizing
    df['lb'] = gp.transform('quantile', 0.01)
    df['ub'] = gp.transform('quantile', 0.99)
    
    # Winsorize
    df['val_wins'] = df['val'].clip(upper=df['ub'], lower=df['lb'])
    

    输出

    大多数行不会更改(仅 1-99 个百分位数之外的行),因此我们可以检查发生更改的小 susbet 行以查看其是否有效。您可以看到相同月份的行具有相同的界限,并且 Winsorized 值 ('val_wins') 被正确地剪裁到它超出的界限。

    df[df['val'] != df['val_wins']]
    
    #           date       val        lb        ub  val_wins
    #42   2010-09-01 -1.686566 -1.125862  3.206333 -1.125862
    #96   2010-04-01 -1.255322 -1.243975  2.995711 -1.243975
    #165  2010-08-01  3.367880 -1.020273  3.332030  3.332030
    #172  2010-09-01 -1.813011 -1.125862  3.206333 -1.125862
    #398  2010-09-01  3.281198 -1.125862  3.206333  3.206333
    #...         ...       ...       ...       ...       ...
    #9626 2010-12-01  3.626950 -1.198967  3.249161  3.249161
    #9746 2010-11-01  3.472490 -1.259557  3.261329  3.261329
    #9762 2010-09-01  3.460467 -1.125862  3.206333  3.206333
    #9768 2010-06-01 -1.625013 -1.482529  3.295520 -1.482529
    #9854 2010-12-01 -1.475515 -1.198967  3.249161 -1.198967
    #
    #[214 rows x 5 columns]
    

    【讨论】:

    • 首先感谢您的帮助。其次,我在您的解决方案中遇到了一些意想不到的结果。我编辑了问题以澄清预期和实际结果是什么。
    • @t.pellegrom 因此,对于有限的数据(以及每组的少量观察),需要对分位数进行插值。例如pd.Series(range(5)).quantile(0.01)返回0.04(尽管值是[0,1,2,3,4],因为默认是线性插值。你可以尝试设置下限使用interpolation='lower'和上限使用interpolation='higher'获取分位数时, 那么当你得到 Winsorized Series 的分位数时,如果你使用 'lower',那么下分位数会同意,如果你使用 'higher',上分位数会同意
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-02
    • 2021-03-31
    相关资源
    最近更新 更多