如何更高效地计算滚动协方差

11

我正在尝试在R中计算一组数据(我的x变量的每一列)和另一个数据(y变量)之间的滚动协方差。我认为可以使用其中一个apply函数,但找不到如何同时滚动两个输入集的方法。这是我尝试过的:

 set.seed(1)
 x<-matrix(rnorm(500),nrow=100,ncol=5)
 y<-rnorm(100)
 rollapply(x,width=5,FUN= function(x) {cov(x,y)})
 z<-cbind(x,y)
 rollapply(z,width=5, FUN=function(x){cov(z,z[,6])})

但是没有一个达到我想要的效果。我找到的一个解决方法是使用 for 循环,但想知道在 R 中是否有更高效的方法:

但是没有一个达到我想要的效果。我发现一个解决方案是使用 for 循环,但是想知道在 R 中是否有更高效的方法:

dResult<-matrix(nrow=96,ncol=5)
for(iLine in 1:96){
    for(iCol in 1:5){
        dResult[iLine,iCol]=cov(x[iLine:(iLine+4),iCol],y[iLine:(iLine+4)])
    }
}

这使我得到了预期的结果:

head(dResult)


           [,1]       [,2]        [,3]        [,4]        [,5]
[1,]  0.32056460 0.05281386 -1.13283586 -0.01741274 -0.01464430
[2,] -0.03246014 0.78631603 -0.34309778  0.29919297 -0.22243572
[3,] -0.16239479 0.56372428 -0.27476604  0.39007645  0.05461355
[4,] -0.56764687 0.09847672  0.11204244  0.78044096 -0.01980684
[5,] -0.43081539 0.01904417  0.01282632  0.35550327  0.31062580
[6,] -0.28890607 0.03967327  0.58307743  0.15055881  0.60704533

1
第一篇文章写得非常详细,做得很好。 - the_skua
4个回答

8

这是一个使用rollapply()sapply()的解决方案:

sapply(1:5, function(j) rollapply(1:100, 5, function(i) cov(x[i, j], y[i])))

我认为这个解决方案比使用for循环更易读,并且更类似于R语言,但是我用microbenchmark检测了一下,它似乎更慢。


哦,我明白我的错误了(我的R还在忙)干得好!+1 - Jan Sila
确实可以工作,谢谢。我想我们没有节省时间,因为你在这里以某种方式创建了两个索引循环,而不是直接将_cov_函数应用于x和y数据,就像你可以使用_mapply_一样。 - Djiggy

8
set.seed(1)
x<-as.data.frame(matrix(rnorm(500),nrow=100,ncol=5))
y<-rnorm(100)

library(zoo)

covResult = sapply(x,function(alpha) {

cov_value = rollapply(cbind(alpha,y),width=5,FUN = function(beta) cov(beta[,1],beta[,2]),by.column=FALSE,align="right") 

return(cov_value)

})

head(covResult)
#              V1         V2          V3          V4          V5
#[1,]  0.32056460 0.05281386 -1.13283586 -0.01741274 -0.01464430
#[2,] -0.03246014 0.78631603 -0.34309778  0.29919297 -0.22243572
#[3,] -0.16239479 0.56372428 -0.27476604  0.39007645  0.05461355
#[4,] -0.56764687 0.09847672  0.11204244  0.78044096 -0.01980684
#[5,] -0.43081539 0.01904417  0.01282632  0.35550327  0.31062580
#[6,] -0.28890607 0.03967327  0.58307743  0.15055881  0.60704533

同时也请查看:

library(PerformanceAnalytics)
?chart.rollingCorrelation

这正是我在第二次尝试中想要使用_z_变量做的事情,但我对_apply_函数的掌握仍然太有限了。非常感谢! - Djiggy
如果我对@Stibu的评论是正确的,那么我猜这个应该比我做的_for_循环更快。 - Djiggy

3

如果您需要更快的速度,并且不需要cov的任何非默认参数,则可以使用TTR::runCov。请注意,默认情况下会填充前导的NA

在更大的数据上,速度差异将更为重要。以下是如何使用它的示例:

cov_joshua <- function() {
  apply(x, 2, function(x, y) TTR::runCov(x, y, 5), y = y)
}

以下是使用OP提供的小数据集与当前已接受答案进行比较:

cov_osssan <- function() {
  f <- function(b) cov(b[,1], b[,2])
  apply(x, 2, function(a) {
    rollapplyr(cbind(a,y), width=5, FUN = f, by.column=FALSE)
  })
}
require(zoo)  # for cov_osssan
require(microbenchmark)
set.seed(1)
nr <- 100
nc <- 5
x <- matrix(rnorm(nc*nr),nrow=nr,ncol=nc)
y <- rnorm(nr)
microbenchmark(cov_osssan(), cov_joshua())
# Unit: milliseconds
#          expr       min        lq    median       uq      max neval
#  cov_osssan() 22.881253 24.569906 25.625623 27.44348 32.81344   100
#  cov_joshua()  5.841422  6.170189  6.706466  7.47609 31.24717   100
all.equal(cov_osssan(), cov_joshua()[-(1:4),])  # rm leading NA
# [1] TRUE

现在,使用更大的数据集:

system.time(cov_joshua())
#    user  system elapsed 
#   2.117   0.032   2.158 
system.time(cov_osssan())
# ^C
# Timing stopped at: 144.957 0.36 145.491 

我等了大约2.5分钟后,cov_osssan 还没有完成,我感到有些疲倦。


1

现在我正在运行一些长时间的模拟,所以不能使用R,但是认为这应该可以解决。外部apply按列将取出该列并传递给rollapply,在那里它将被用于与y进行滚动窗口协方差计算。希望如此 :D

apply(x,2,function(x) rollapply(x,width=5,function(z) cov(x,y)))

这样不行。你总是在rollaply()函数内计算完整向量x和y的协方差。因此,每一列都包含相同的值重复96次。 - Stibu

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