每月 Winsorize dataframe 列,同时忽略 NaN 的
Winsorize dataframe columns per month while ignoring NaN's
我有一个包含每月数据和以下列的数据框:日期、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
P.S。我意识到我没有包含所需的输出,但我希望所需的输出是明确的。否则我可以尝试把一些东西放在一起。
编辑: 来自@Allolz 的这些解决方案已经很有帮助,但它并没有完全按照预期工作。在我 运行 @Allolz I I 运行 的代码之前:
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
缩尾后我得到:
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 有关,但我不确定这是否真的是原因。
一种更快的方法要求您创建辅助列。我们将使用 groupby
+ transform
将 0.01 和 0.99 分位数(对于该月份组)的列广播回 DataFrame,然后您可以使用这些系列立即剪辑原始数据。 (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]
我有一个包含每月数据和以下列的数据框:日期、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
P.S。我意识到我没有包含所需的输出,但我希望所需的输出是明确的。否则我可以尝试把一些东西放在一起。
编辑: 来自@Allolz 的这些解决方案已经很有帮助,但它并没有完全按照预期工作。在我 运行 @Allolz I I 运行 的代码之前:
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
缩尾后我得到:
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 有关,但我不确定这是否真的是原因。
一种更快的方法要求您创建辅助列。我们将使用 groupby
+ transform
将 0.01 和 0.99 分位数(对于该月份组)的列广播回 DataFrame,然后您可以使用这些系列立即剪辑原始数据。 (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]