将 DataFrame 中每一列的值设置为第 90 个百分位数

Set value to 90th percentile for each column in a DataFrame

我正在处理看起来像

描述的 DataFrame 的数据
df = pd.DataFrame({'AAA': [1,1,1,2,2,2,3,3], 'BBB': [2,1,3,4,6,1,2,3]})

我想做的是,如果值超过第 90 个百分位数,则将值设置为四舍五入 (90%)。所以这就像将最大值限制在第 90 个百分位数。

这对我来说越来越棘手,因为每一列都会有不同的百分位值。

我可以使用以下方法获得第 90 个百分位值:

df.describe(percentiles=[.9])

所以对于 BBB 列,6 大于 4.60(第 90 个百分位数),因此需要将其更改为 5(汇总 4.60)。

在我的实际问题中,我正在为一个大矩阵做这个,所以我想知道是否有任何简单的解决方案,而不是先创建一个第 90 个百分位列的数组,然后检查元素一列并将其设置为向上舍入到第 90 个百分位数。

执行此操作的一种方法是 clip_upper() 对每列的第 90 个百分位值 np.percentile(x, 90) 应用

In [242]: df.apply(lambda x: x.clip_upper(np.percentile(x, 90)))
Out[242]:
   AAA  BBB
0    1  2.0
1    1  1.0
2    1  3.0
3    2  4.0
4    2  4.6
5    2  1.0
6    3  2.0
7    3  3.0

我曾想象@ajcr 优雅的解决方案会比 apply 更快。 但是,

低于 len(df) ~ 130K

的基准
In [245]: %timeit df.apply(lambda x: x.clip_upper(np.percentile(x, 90)))
100 loops, best of 3: 7.49 ms per loop

In [246]: %timeit np.minimum(df, df.quantile(0.9))
100 loops, best of 3: 11.1 ms per loop

len(df) ~ 1M

In [248]: %timeit df.apply(lambda x: x.clip_upper(np.percentile(x, 90)))
10 loops, best of 3: 54.5 ms per loop

In [249]: %timeit np.minimum(df, df.quantile(0.9))
10 loops, best of 3: 73.9 ms per loop

一种矢量化方法是结合 np.minimum and df.quantile:

>>> np.minimum(df, df.quantile(0.9))
   AAA  BBB
0    1  2.0
1    1  1.0
2    1  3.0
3    2  4.0
4    2  4.6
5    2  1.0
6    3  2.0
7    3  3.0

要获得更大的速度提升,请使用:

np.minimum(df, np.percentile(df, 90, axis=0))

df.quantile 似乎比 np.percentile 慢(可能是因为它 returns 是一个系列而不是普通的 NumPy 数组)。