Pandas 滚动应用自定义函数

32

我一直在跟着这个类似的答案(这里),但是当使用sklearn和rolling apply时我有一些问题。我尝试使用rolling apply创建z-score并进行PCA,但是我一直收到'only length-1 arrays can be converted to Python scalars' error.的错误信息。

按照之前的示例,我创建了一个数据框。

from sklearn.preprocessing import StandardScaler
import pandas as pd
import numpy as np
sc=StandardScaler() 
tmp=pd.DataFrame(np.random.randn(2000,2)/10000,index=pd.date_range('2001-01-01',periods=2000),columns=['A','B'])

如果我使用rolling命令:

 tmp.rolling(window=5,center=False).apply(lambda x: sc.fit_transform(x))
 TypeError: only length-1 arrays can be converted to Python scalars

我遇到了这个错误,但是我可以轻松创建具有平均值和标准偏差的函数,没有任何问题。

def test(df):
    return np.mean(df)
tmp.rolling(window=5,center=False).apply(lambda x: test(x))

我相信错误发生在我尝试用当前值减去均值以计算z-score时。

def test2(df):
    return df-np.mean(df)
tmp.rolling(window=5,center=False).apply(lambda x: test2(x))
only length-1 arrays can be converted to Python scalars

我如何使用sklearn创建自定义滚动函数,以首先标准化,然后运行PCA?

编辑: 我意识到我的问题不太清楚,所以我再试一次。我想要标准化我的值,然后运行PCA以获得每个因子解释的方差量。在没有滚动的情况下,这样做相当简单。

testing=sc.fit_transform(tmp)
pca=decomposition.pca.PCA() #run pca
pca.fit(testing) 
pca.explained_variance_ratio_
array([ 0.50967441,  0.49032559])

在滚动时,我无法使用相同的过程。使用 @piRSquared 的滚动 zscore 函数可以得到 zscores。似乎 sklearn 的 PCA 与滚动应用自定义函数不兼容。(实际上,我认为大多数 sklearn 模块都是这种情况。)我只是试图获取解释方差,这是一个一维项,但以下代码返回了一堆 NaN。

def test3(df):
    pca.fit(df)
    return pca.explained_variance_ratio_
tmp.rolling(window=5,center=False).apply(lambda x: test3(x))

然而,我可以创建自己的解释方差函数,但这也不起作用。

def test4(df):
    cov_mat=np.cov(df.T) #need covariance of features, not observations
    eigen_vals,eigen_vecs=np.linalg.eig(cov_mat)
    tot=sum(eigen_vals)
    var_exp=[(i/tot) for i in sorted(eigen_vals,reverse=True)]
    return var_exp
tmp.rolling(window=5,center=False).apply(lambda x: test4(x))

我遇到了这个错误:给定一个零维数组。数组必须至少有两个维度。

简而言之,我想运行滚动z分数,然后运行滚动主成分分析,并输出每次滚动的解释方差。我已经完成了滚动z分数,但还没有解释方差。


1
你期望输出是什么?Pandas的滚动函数应该从一块输入中产生单个标量值。如果你想在块上执行更复杂的操作,你就必须自己编写"滚动函数"。 - BrenBarn
2个回答

46

正如@BrenBarn所评论的那样,rolling函数需要将一个向量减少为一个数。以下代码等同于您尝试执行的内容,并帮助强调问题。

如上所述

zscore = lambda x: (x - x.mean()) / x.std()
tmp.rolling(5).apply(zscore)
TypeError: only length-1 arrays can be converted to Python scalars
zscore函数中,x.mean()x.std()都会减少,但x是一个数组。因此整个计算结果也是一个数组。
解决这个问题的方法是只对需要进行滚动计算的部分进行操作,而不是对导致问题的那些部分进行操作。
(tmp - tmp.rolling(5).mean()) / tmp.rolling(5).std()

enter image description here


1
感谢提供z-score部分。我尝试在PCA部分做类似的事情,但没有成功。是lambda搞乱了PCA吗?因为我正在处理多行而不仅仅是一行。 - Bobe Kryant

15

由于lambda函数中的x表示一个(滚动)系列/ndarray,因此可以这样编写lambda函数(其中x[-1]指当前滚动数据点):

zscore = lambda x: (x[-1] - x.mean()) / x.std(ddof=1)

那么可以这样称呼:

tmp.rolling(5).apply(zscore)

还需注意,在tmp.rolling(5).std()中自由度默认为1。为了生成与 @piRSquared 相同的结果,必须为x.std()指定ddof,其默认值为0。-找出这个问题花了相当长的时间!


8
嗨Jerry,我尝试使用你的答案,但在x[-1]处出现了Key Error错误。 x属于pandas.core.series.Series类。使用x.values[-1]解决了我的问题。 - Steven
如果将参数center=True传递给rolling函数,这个说法仍然正确吗? - guillaume latour

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