【问题标题】:matrix multiplication without losing xts properties矩阵乘法而不丢失 xts 属性
【发布时间】:2020-05-30 22:22:21
【问题描述】:

我有一个xts 对象,我希望创建列的加权总和(并且这样做LOT)。到目前为止,最简单的方法是矩阵乘法,但结果失去了良好的xts 品质。

通过创建一个新的xts 对象很容易将它们添加回来——但这既慢又乏味。

例如:

dd <- xts(matrix(rnorm(200), ncol=2), Sys.Date() + 1:100)
w_sum <- dd %*% c(-1, 1)

...问题是:

> tail(w_sum)
             [,1]
 [95,]  0.1758262
 [96,] -0.3310975
 [97,] -0.1204836
 [98,] -1.2242001
 [99,] -1.7333222
[100,]  1.1216603

解决方法是:

w_sumx <- xts(dd %*% c(-1, 1), index(dd))

但这不仅麻烦,而且速度很慢。另外,我感兴趣地注意到xts 的减法速度非常快。有没有办法利用xts 的快速内部结构来做到这一点?

f1 <- function() xts(dd %*% c(-1, 1), index(dd))
f2 <- function() dd[,2] - dd[,1]

> microbenchmark::microbenchmark(f1(), f2(), times = 1000)
Unit: microseconds
 expr  min   lq     mean median     uq    max neval cld
 f1() 83.7 97.3 114.1294 104.65 115.00 6688.4  1000   b
 f2() 26.3 34.0  40.6202  38.85  45.15  155.4  1000  a 

【问题讨论】:

  • w_sum 保留为dd 中的列是否可行?如果是这样,dd$w_sum &lt;- dd %*% c(-1, 1) 可能是最快的(尽管您需要 dd 的命名列才能使其工作,您的示例中没有,但可能在现实生活中)。
  • 这样设置是可能的——但我希望它不这样做也能正常工作。我突然想到,我可以重载 %*% 并使用 Rcpp 使其快速......尽管这看起来像是一把大锤
  • w_sum &lt;- Reduce("+", as.list(dd * rep(c(-1, 1), each = nrow(dd))))f1 快一点 - 出于某种原因,它设法保留了 xts 类。
  • 这似乎更快。 replace(dd[, 1], 1:nrow(dd), dd %*% c(-1, 1))

标签: r xts


【解决方案1】:

存在一些简单的替代方案。显然您可以按照建议重写Rcpp 中的方法,但更简单的替代方法是在执行矩阵正则乘法后覆盖属性。

dd_new <- dd %*% c(-1, 1)
att <- attributes(dd)
att$dim <- dim(dd_new)
attributes(dd_new) <- att

这不如纯矩阵乘法快,但比时间序列本身的子集快约 10 - 13 倍。

microbenchmark::microbenchmark(xts = dd[, 1] - dd[, 2], 
                               matmult = dd %*% c(1, -1),
                               xtsmatmult = xts(dd %*% c(1, -1), index(dd)),
                               "%.%" = dd %.% c(1, -1),
                               "%-%" = dd %-% c(1, -1),
                               times = 1e5)
Unit: milliseconds
       expr    min     lq    mean median     uq    max neval
        xts 0.0396 0.0685 0.11200 0.0998 0.1170  15.40 1e+05
    matmult 0.0008 0.0021 0.00352 0.0028 0.0040   7.71 1e+05
 xtsmatmult 0.0853 0.1380 0.22900 0.2100 0.2300 117.00 1e+05
        %.% 0.0025 0.0055 0.00905 0.0076 0.0099   8.97 1e+05
        %-% 0.0096 0.0183 0.03030 0.0268 0.0318 101.00 1e+05

在上面的%.%是一个准系统函数,只留下矩阵乘法和覆盖属性,而%-%添加了一些简单的输入检查,以确保尺寸是可接受的,并使用S3类样式,为了简化概括。

功能:

请注意,compiler::cmpfun 函数已用于对函数进行字节编译(类似于包函数)。在这种情况下,效果是微不足道的。

`%.%` <- compiler::cmpfun(function(x, z){
    x2 <- x %*% z
    att <- attributes(x)
    att$dim <- dim(x2)
    attributes(x2) <- att
    x2
})
`%-%` <- function(x, z)
    UseMethod('%-%')
`%-%.xts` <- compiler::cmpfun(function(x, z){
    ## 
    if(!is.xts(x))
        stop('x must be an xts object')
    if(!is.numeric(z) || !(n <- length(z)) == ncol(x) || n == 0)
        stop('z must be an index vector')
    x2 <- x %*% z
    att <- attributes(x)
    att$dim <- dim(x2)
    attributes(x2) <- att
    x2
})

【讨论】:

  • 必须承认我不理解我的答案的反对意见,因为它似乎考虑了所提出的每一个问题,同时保持所有方法都可用于结果。
  • 我发现代码坏了。我从%.%%-% 收到错误消息Error: evaluation nested too deeply: infinite recursion / options(expressions=)?
  • 有趣。这两个函数没有递归。错误消息通常是由永远运行的代码引起的(answer 很好地解释了它)。我的猜测是你的代码的另一部分导致了一个几乎无限的循环(你说你必须这样做“很多”,问题是这个 ALOT 是否会导致无限递归)。
  • 哦,我明白了;我把它写成%*%.xts ...有没有可能在我的版本中它在x2 &lt;- x %*% z步骤中调用自己?
  • 这很有可能。尝试使用新的 R-session 并注释掉 %*%.xts 函数。 (R-studio 中多行注释的快捷键应该是 ctrl+shift+C)。希望这能解决问题。
猜你喜欢
  • 2011-11-23
  • 2012-06-01
  • 1970-01-01
  • 2012-12-29
  • 1970-01-01
  • 2012-07-10
  • 1970-01-01
  • 2018-04-11
  • 2017-03-11
相关资源
最近更新 更多