numpy Pandas滚动-应用于计算相关性

7gcisfzg  于 2023-08-05  发布在  其他
关注(0)|答案(1)|浏览(111)

这个问题与我在这里的另一个问题有关。
我想计算pandas数据框架中两列之间的滚动指数加权相关性。

import pandas as pd
import numpy as np

n_obs = 10000
window = 260
min_obs = 130
halflife = 130

# create a dummy dataframe
df = pd.DataFrame(np.random.rand(n_obs, 2), columns=['A', 'B'])

# initialize weights
w = 0.5**((window-np.arange(window)-1)/(halflife-1))

# correlation for a single sliding window. 
# note because of `min_obs`, df_win might be of various lengths
def _corr_single_window_(df_win):
    return df_win.mul(w[-df_win.shape[0]:], axis=0).corr().iloc[0, 1]

# test on the dummy dataframe
df.rolling(window=window, min_periods=min_obs).apply(_corr_single_window_)

字符串
我得到了这个错误:
DataError:没有要聚合的数值类型

im9ewurl

im9ewurl1#

您收到此错误的原因是因为df_win实际上是pd.Series.rolling.apply不通过pd.DataFrame,因此相关性计算是不可能的-因为只有一个数据系列。在this answer中有更多的解释。
以下是计算问题中相关性的几个选项:

选项1 -滚动申请

使用与您几乎相同的代码,我将其改为在返回代码的开头使用全局变量df,以便使用两列,而不是df_win中的单个列(因为这是一个pd.Series。此外,我还将调用函数的行更改为仅对单个列运行,否则输出将返回两次。

def _corr_single_window_(df_win):
    return df.loc[df_win.index].mul(w[-df_win.shape[0]:], axis=0).corr().iloc[0, 1]

df.rolling(window=window, min_periods=min_obs)["A"].apply(_corr_single_window_)

# 18.2 s ± 437 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

字符串

选项2 -列表解析

这个列表解析计算min_obs有一个if else语句,并使用max(0, i-window)作为.iloc的下限,因此它从min_obs的开始增加到完整的window长度。如果您使用的是Python>=3.8,则可以在中使用"walrus operator"来避免重复此计算。

pd.Series([df.iloc[max(0, i-window): i]
           .mul(w[-(i-max(0, i-window)):], axis=0)
           .corr().iloc[0, 1]
           if i>=min_obs
           else np.nan
           for i in range(1, len(df) + 1)])
# 12.8 s ± 263 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
# (or with walrus operators for Python >= 3.8)
pd.Series([df.iloc[(min_idx := max(0, i-window)): i]
           .mul(w[-(i-min_idx):], axis=0)
           .corr().iloc[0, 1]
           if i>=min_obs
           else np.nan
           for i in range(1, len(df)+1)])

# 12.8 s ± 263 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

选项3 -带卷管

主要改编自this answer,它使用.pipe将函数链接在一起。

def rolling_pipe(dataframe, window, fctn):
    return pd.Series([dataframe.iloc[max(0, i-window): i].pipe(fctn) 
                      if i >= window else None 
                      for i in range(1, len(dataframe)+1)],
                     index = dataframe.index) 

df.pipe(rolling_pipe, window,
        lambda x: x.mul(w[-x.shape[0]:], axis=0).corr().iloc[0, 1])

# 7.66 s ± 1.9 s per loop (mean ± std. dev. of 7 runs, 1 loop each)


从上面的时序可以看出,第三个选项是最有效的。然而,鉴于大量的观察结果,仅进行了7次运行,并且多次测试给出了相当不同的时间。

相关问题