Как более эффективно вычислять скользящую ковариацию
Я пытаюсь вычислить скользящую ковариацию между набором данных (каждый столбец моей переменной x) и другой (переменной y) в R. Я думал, что могу использовать одну из применяемых функций, но не могу найти способ одновременно катить два набора входов. Вот что я пробовал:
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, чем:
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
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
Ответ 2
Это решение с rollapply()
и sapply()
:
sapply(1:5, function(j) rollapply(1:100, 5, function(i) cov(x[i, j], y[i])))
Я думаю, что это более читаемо и больше R-ish, чем решение с for-loops, но я проверил с помощью microbenchmark
и, похоже, медленнее.
Ответ 3
Сейчас я запускаю длинные симуляции, поэтому не могу использовать R, но считайте, что это должно сработать. Внешнее применение по столбцам займет столбец, передаст его в rollapply, где он будет использоваться для ковариации качения окна с y. Надеюсь: D
apply(x,2,function(x) rollapply(x,width=5,function(z) cov(x,y)))
Ответ 4
Если вам нужно что-то быстрее, и вам не нужны никакие аргументы, отличные от значения по умолчанию, для 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
для завершения.