Pandas 每组中前 N% 的值,同时忽略缺失值

Pandas Top N% of values within each group while ignoring missing values

我有一个 df,我想为每一行(对应一个月)列出该行中“B”最高值的 50%。

month A B
1994-07 A 50
1994-07 B 60
1994-07 C 70
1994-07 D 80
1994-07 E NAN
1994-07 F NAN
1994-08 A 90
1994-08 B 60
1994-08 C 70
1994-08 D 95
1994-08 E 100
1994-08 F 110
1994-08 G NAN

对于July/1994,我只有 4 列“B”填充了值,因此 50% 将是 2 个最高的 MV。之后的一个月,我有 6 只股票,这给了我 3 个最高值:

month A B
1994-07 C 70
1994-07 D 80
1994-08 D 95
1994-08 E 100
1994-08 F 110

我试过:

df = df.groupby(pd.Grouper(freq="M")).apply(lambda g: g.nsmallest(len(g)//2, 'B'))

但是,它不会忽略“NAN”并将其计为一个数字。例如,对于 July/1994,它计算 6 个值,因此它 return 是当月最高值的 3 个(6 个的 50%)。相反,它应该算作有 4 个值,return 我是最高的 2 个。

IIUC,你想使用 g['B'].count() 因为 count 忽略 NaNs:

(df
 .groupby('month')
 .apply(lambda g: g.nlargest(g['B'].count()//2, columns='B'))
 .droplevel(0).sort_index()
)

或者,dropna 首先:

(df
 .dropna(subset='B')
 .groupby('month')
 .apply(lambda g: g.nlargest(len(g)//2, 'B'))
 .droplevel(0).sort_index()
)

输出:

      month  A      B
2   1994-07  C   70.0
3   1994-07  D   80.0
9   1994-08  D   95.0
10  1994-08  E  100.0
11  1994-08  F  110.0

或者,您可以将 median 传递给 groupby.transform,然后过滤掉大于中位数(即前 50%)的值。由于 median 方法默认跳过 NaN,因此没有问题。

out = df[df['B'] > df.groupby('month')['B'].transform('median')]

输出:

      month  A      B
2   1994-07  C   70.0
3   1994-07  D   80.0
9   1994-08  D   95.0
10  1994-08  E  100.0
11  1994-08  F  110.0