Python 最优化中的EWMA协方差矩阵
我想使用Pandas从股票价格回报的数据框架中计算EWMA协方差矩阵,并遵循中的方法 我喜欢使用Pandas对象和函数的灵活性,但当资产集增长时,功能变得非常缓慢:Python 最优化中的EWMA协方差矩阵,python,pandas,python-multiprocessing,covariance,Python,Pandas,Python Multiprocessing,Covariance,我想使用Pandas从股票价格回报的数据框架中计算EWMA协方差矩阵,并遵循中的方法 我喜欢使用Pandas对象和函数的灵活性,但当资产集增长时,功能变得非常缓慢: import pandas as pd import numpy as np def ewma_cov_pairwise_pd(x, y, alpha=0.06): x = x.mask(y.isnull(), np.nan) y = y.mask(x.isnull(), np.nan) covariati
import pandas as pd
import numpy as np
def ewma_cov_pairwise_pd(x, y, alpha=0.06):
x = x.mask(y.isnull(), np.nan)
y = y.mask(x.isnull(), np.nan)
covariation = ((x - x.mean()) * (y - y.mean()).dropna()
return covariation.ewm(alpha=0.06).mean().iloc[-1]
def ewma_cov_pd(rets, alpha=0.06):
assets = rets.columns
n = len(assets)
cov = np.zeros((n, n))
for i in range(n):
for j in range(i, n):
cov[i, j] = cov[j, i] = ewma_cov_pairwise_pd(
rets.iloc[:, i], rets.iloc[:, j], alpha=alpha)
return pd.DataFrame(cov, columns=assets, index=assets)
我希望在仍然使用Pandas的情况下提高代码的速度,但瓶颈在DataFrame.ewm()函数中,该函数使用了90%的计算时间
如果使用此函数是一种绑定约束,那么提高代码运行速度的最有效方法是什么?我正在考虑采用蛮力方法并使用concurrent.futures.ProcessPoolExecutor,但也许有更好的解决方案
n = 100 # n is typically 2000
rets = pd.DataFrame(np.random.normal(0, 1., size=(n, n)))
cov_pd = ewma_cov_pd(rets)
真正的时间序列数据可能包含前导空值和之后可能丢失的值,尽管后者不太可能
更新I
一个潜在的解决方案可以利用广亨提供的答案,并在更合理的时间内产生预期的结果,类似于:
def ewma_cov_frame_qh(rets, alpha=0.06):
weights = (1-alpha) ** np.arange(len(df))[::-1]
normalized = (rets-rets.mean()).to_numpy()
out = (weights * normalized.T) @ normalized / weights.sum()
return pd.DataFrame(out, index=rets.columns, columns=rets.columns)
def ewma_cov_qh(rets, alpha=0.06):
syms = rets.columns
covar = pd.DataFrame(index=rets.columns, columns=rets.columns)
delta = rets.isnull().sum(axis=1).shift(1) - rets.isnull().sum(axis=1)
dates = delta.loc[delta != 0].index.tolist()
for date in dates:
frame = rets.loc[rets.index >= date].dropna(axis=1, how='any')
cov = ewma_cov_frame_qh(frame).reindex(index=syms, columns=syms)
covar = covar.fillna(cov)
return covar
cov_qh = ewma_cov_qh(rets)
这违反了使用本机Pandas/Numpy函数计算基础协方差的要求,并且计算时间将取决于数据集中前导na的数量
更新II
在我的机器上,使用(简单的)多处理并将计算时间进一步提高42.5%的潜在改进如下所示:
from concurrent.futures import ProcessPoolExecutor, as_completed
from functools import partial
def ewma_cov_mp_worker(date, rets, alpha=0.06):
syms = rets.columns
frame = rets.loc[rets.index >= date].dropna(axis=1, how='any')
return ewma_cov_frame_qh(frame, alpha=alpha).reindex(index=syms, columns=syms)
def ewma_cov_mp(rets, alpha=0.06):
covar = pd.DataFrame(index=rets.columns, columns=rets.columns)
delta = rets.isnull().sum(axis=1).shift(1) - rets.isnull().sum(axis=1)
dates = delta.loc[delta != 0].index.tolist()
func = partial(ewma_cov_mp_worker, rets=rets, alpha=alpha)
covs = {}
with ProcessPoolExecutor(max_workers=6) as exec:
future_to_date = {exec.submit(func, date): date for date in dates}
covs = {future_to_date[future]: future.result() for future in as_completed(future_to_date)}
for date in dates:
covar.fillna(covs[date], inplace=True)
return covar
[我没有添加答案,因为没有解决原始问题,我很乐观有更好的解决方案。]因为您并不真正关心
ewm
,也就是说,您只取最后一个值。我们可以尝试矩阵乘法:
def ewma(df, alpha=0.94):
weights = (1-alpha) ** np.arange(len(df))[::-1]
# fillna with 0 here
normalized = (df-df.mean()).fillna(0).to_numpy()
out = ((weights * normalized.T) @ normalized / weights.sum()
return out
# verify
out = ewma(df)
print(out[0,1] == ewma_cov_pairwise(df[0],df[1]) )
# True
在我的系统上,使用
df.shape==(20002000)
大约需要150毫秒,而您的代码拒绝在几分钟内运行:-)。因为您并不真正关心ewm
,也就是说,您只取最后一个值。我们可以尝试矩阵乘法:
def ewma(df, alpha=0.94):
weights = (1-alpha) ** np.arange(len(df))[::-1]
# fillna with 0 here
normalized = (df-df.mean()).fillna(0).to_numpy()
out = ((weights * normalized.T) @ normalized / weights.sum()
return out
# verify
out = ewma(df)
print(out[0,1] == ewma_cov_pairwise(df[0],df[1]) )
# True
这在我使用df.shape==(20002000)
的系统上大约花费了150 ms
,而您的代码拒绝在几分钟内运行:-)。谢谢Quang和您的建议。问题是数据可能包含前导缺失值(我在上面没有提到)。因此,在您的示例中,如果df.iloc[0,0]=np.nan,那么将不起作用。我想我可以递归地遍历。另外,如果我将alpha替换为**kwargs,则可以利用本机函数中的任何其他参数。@user2579685将nan填充为0不起作用?我不知道当你有了领导权后会有什么变化。发生这种情况时,您会期望什么?我想计算x和y上的公共观测值的成对covar。@user2579685更新,我测试了设置df.loc[:5,0]=np.nan
,并使用fillna
,如答案所示。这两种方法返回相同的答案。但在不使用零填充na的情况下,pandas函数会得到我想要的结果,如果我将函数分解为两两运算,则改进会比您预期的要温和得多。谢谢Quang,就像您所建议的那样。问题是数据可能包含前导缺失值(我在上面没有提到)。因此,在您的示例中,如果df.iloc[0,0]=np.nan,那么将不起作用。我想我可以递归地遍历。另外,如果我将alpha替换为**kwargs,则可以利用本机函数中的任何其他参数。@user2579685将nan填充为0不起作用?我不知道当你有了领导权后会有什么变化。发生这种情况时,您会期望什么?我想计算x和y上的公共观测值的成对covar。@user2579685更新,我测试了设置df.loc[:5,0]=np.nan
,并使用fillna
,如答案所示。这两种方法返回相同的答案。但是,如果不使用零填充na,pandas函数将得到我想要的结果,如果我将函数分解为成对操作,则改进将比您预期的要温和得多。