熊猫滚动窗口百分位数排名

10

我正在尝试计算滚动窗口内每列数据的百分位排名。

test=pd.DataFrame(np.random.randn(20,3),pd.date_range('1/1/2000',periods=20),['A','B','C'])

test
Out[111]: 
                   A         B         C
2000-01-01 -0.566992 -1.494799  0.462330
2000-01-02 -0.550769 -0.699104  0.767778
2000-01-03 -0.270597  0.060836  0.057195
2000-01-04 -0.583784 -0.546418 -0.557850
2000-01-05  0.294073 -2.326211  0.262098
2000-01-06 -1.122543 -0.116279 -0.003088
2000-01-07  0.121387  0.763100  3.503757
2000-01-08  0.335564  0.076304  2.021757
2000-01-09  0.403170  0.108256  0.680739
2000-01-10 -0.254558 -0.497909 -0.454181
2000-01-11  0.167347  0.459264 -1.247459
2000-01-12 -1.243778  0.858444  0.338056
2000-01-13 -1.070655  0.924808  0.080867
2000-01-14 -1.175651 -0.559712 -0.372584
2000-01-15 -0.216708 -0.116188  0.511223
2000-01-16  0.597171  0.205529 -0.728783
2000-01-17 -0.624469  0.592436  0.832100
2000-01-18  0.259269  0.665585  0.126534
2000-01-19  1.150804  0.575759 -1.335835
2000-01-20 -0.909525  0.500366  2.120933

我尝试使用.rolling和.apply,但是我似乎遗漏了一些东西。

pctrank = lambda x: x.rank(pct=True)
rollingrank=test.rolling(window=10,centre=False).apply(pctrank)

对于A列,最终值将是从2000-01-11到2000-01-20的长度为10的窗口内-0.909525的百分位排名。有任何想法吗?

3个回答

7

你的lambda函数接收一个numpy数组,该数组没有.rank方法——它是pandas的SeriesDataFrame才有这个方法。因此,你可以将其更改为:

pctrank = lambda x: pd.Series(x).rank(pct=True).iloc[-1]

或者你可以使用纯numpy,按照这个SO答案的思路:

def pctrank(x):
    n = len(x)
    temp = x.argsort()
    ranks = np.empty(n)
    ranks[temp] = (np.arange(n) + 1) / n
    return ranks[-1]

太完美了,正是我想要的。谢谢。所以在应用rank()之前,我忘了将x定义为panda Series。你能解释一下为什么我的lambda收到一个numpy数组吗?.rolling()和.apply()都是作用于panda数据框'test'的panda函数吗? - user6435943
为什么我的lambda会接收到一个numpy数组?我不知道为什么,只是知道错误信息显示类似"numpy.ndarray没有rank方法"。rolling是pandas SeriesDataFrame的方法。apply有几种不同的实现方式。请查看split-apply-combine文档。 - Alicia Garcia-Raboso
x.argsort()的时间复杂度为O(nlogn),但我认为在数组中特定一个值的百分位排名应该是O(n)。 - PaleNeutron

3
最简单的选择是这样做:
from scipy import stats
# 200 is the window size

dataset[name] =  dataset[name].rolling(200).apply(lambda x: stats.percentileofscore(x, x[-1]))

1
如果将scipy.stats中的x更改为x.to_numpy()x.values,则这将有效运行。 - GoneAsync

1
如果你只需要最后一个观测值的排名,就像rolling apply一样,你可以使用以下方法:
 def pctrank(x):
    i = x.argsort().argmax() + 1
    n = len(x)
    return i/n

时间大约快了两倍。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接