I would like to generate covariance matrices (and mean vectors) using a rolling window. But in all my attempts rollapply
stacks the covariance matrices from cov
and runs out of pre-allocated space (e.g., if my original data have 40 observations, then rollapply
can't return more than 40 rows).
Is there a way that I can get rollapply
to return a list of matrices? Or to return a data.frame
that is larger than the original data.frame
, which I can manually split into a list? My end goal is to take a panel, split the panel into a list of individual data.frame
s, calculate the rolling covariances and means for each data frame, then use these lists of covariances and means downstream to compare to a bunch of individuals.
Here is some code. My problem is that my.fun
won't return data from all covariance matrix caluclations. Is my best option to code my own rollapply
? Or my own cov
that returns a vector that I convert back to a matrix? Thanks!
library("zoo")
data.df <- data.frame(sic = rep(1:10, each = 40),
year = rep(1:40, len = 10*40),
one = rnorm(10*40),
two = 2*rnorm(10*40),
three = 3*rnorm(10*40))
data.list <- split(data.df, data.df$sic)
data.list <- lapply(data.list, zoo)
my.fun <- function(x) {
x <- x[, c("one", "two", "three")]
rollapply(x,
width = 10,
FUN = cov,
by.column = F,
align = "right")
}
cov.list <- lapply(data.list, FUN = my.fun)
After glancing at the rollapply.zoo
code, I don't think there's a way to make it do what you want. Rolling your own function isn't that difficult though (pun intended).
rollcov <- function(x, width=10) {
len <- NROW(x)
add <- rep(1:(len-width)-1,each=width)
seq.list <- split(rep(1:width,len-width)+add, add)
lapply(seq.list, function(y) cov(x[y,]))
}
rollcov(data.list[[1]][,c("one","two","three")],10)
all <- lapply(data.list, function(x) rollcov(x[,c("one","two","three")],10))
I realized that I can trick rollapply into returning a vector, then bend that vector back into a matrix. The trick is using alply
from the plyr
package to bend the vector back into a matrix.
library("plyr")
library("zoo")
data.df <- data.frame(sic = rep(1:10, each = 40),
year = rep(1:40, len = 10*40),
one = rnorm(10*40),
two = 2*rnorm(10*40),
three = 3*rnorm(10*40))
data.list <- split(data.df, data$sic)
data.list <- lapply(data.list, zoo)
my.fun <- function(x) {
x <- x[, c("one", "two", "three")]
rollapply(x,
width = 10,
function(x) as.vector(cov(x)),
by.column = F,
align = "right")
}
my.fun.2 <- function(x) {
alply(x, 1, matrix, nrow = sqrt(ncol(x)))
}
cov.list <- lapply(data.list, FUN = my.fun)
cov.list.2 <- lapply(cov.list, my.fun.2)
Or, for a list of arrays I can replace alply
with aaply
.
Remove the second data.list<-
and modify my.fun
so that given data.df
we have the following (which is reasonably close to the original):
data.list <- split(data.df, data.df$sic)
my.fun <- function(x) {
z <- zoo(x[, c("one", "two", "three")], x$year)
ccov <- function(x) c(cov(x))
r <- rollapplyr(z, width = 10, FUN = ccov, by.column = FALSE)
toMat <- function(tt) matrix(r[tt], ncol(z))
sapply(paste(time(r)), toMat, simplify = FALSE) # sapply(char,...) adds names
}
cov.list <- lapply(data.list, FUN = my.fun)
EDIT: Slight simplification.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With