【发布时间】:2021-04-13 07:28:03
【问题描述】:
我正在开展一个新项目,其中包含大型时间序列数据集,从这些数据集相关的计算被馈送到shiny 应用程序中。因此,效率是我感兴趣的。这些操作通常仅限于基本周期转换和风险指标的后续汇总统计。
我正在调查使用哪个库/方法来构建计算脚本。目前,我对xts 和data.table 没问题。虽然我可以使用 quantmod 和 TTR 等库,但我对在生产中部署黑盒功能犹豫不决,更愿意保持完全可追溯性。
到目前为止,我已经进行了以下基准测试,其中将每日价格的data.frame 转换为每月回报。到目前为止使用的包是xts、data.table 和quantmod(作为参考)。代码粘贴在下面,但也可以在 GitHub 上找到。
基准代码
# Simple return exercise: Daily Prices to Monthly Returns
# Input: Nx2 data.frame with columns (N days, price)
# Output: Mx2 object with columns (M months, return)
# Three different functions: 1. xts, 2. data.table, 3. quantmod
rm(list = ls()); gc()
library(data.table)
library(zoo)
library(xts)
library(ggplot2)
library(quantmod)
# Asset params
spot = 100
r = 0.01
sigma = 0.02
N = 1e5
# Input data: Nx2 data.frame (date, price)
pmat = data.frame(
date = seq.Date(as.Date('1970-01-01'), by = 1, length.out = N),
price = spot * exp(cumsum((r - 0.5 * sigma**2) * 1/N + (sigma * (sqrt(1/N)) * rnorm(N, mean = 0, sd = 1))))
)
# Output functions
# 1. xts standalone
xtsfun = function(mat){
xtsdf = as.xts(mat[, 2], order.by = mat[, 1])
eom_prices = to.monthly(xtsdf)[, 4]
mret = eom_prices/lag.xts(eom_prices) - 1; mret[1] = eom_prices[1]/xtsdf[1] - 1
mret
}
# 2. data.table standalone
dtfun = function(mat){
dt = setNames(as.data.table(mat), c('V1', 'V2'))
dt[, .(EOM = last(V2)), .(Month = as.yearmon(V1))][, .(Month, Return = EOM/shift(EOM, fill = first(mat[, 2])) - 1)]
}
# 3. quantmod (black box library)
qmfun = function(mat){
qmdf = as.xts(mat[, 2], order.by = mat[, 1])
monthlyReturn(qmdf)
}
# Check 1 == 2 == 3:
all.equal(
unlist(dtfun(pmat[1:1000,])[, Return]),
as.numeric(xtsfun(pmat[1:1000,])),
as.numeric(qmfun(pmat[1:1000,])),
scale = NULL
)
# Benchmark
library(microbenchmark)
gc()
mbm = microbenchmark(
xts = xtsfun(pmat),
data.table = dtfun(pmat),
quantmod = qmfun(pmat),
times = 50
)
mbm
结果
对于N = 1e5,三种方法的表现相似:
Unit: milliseconds
expr min lq mean median uq max neval
xts 20.62520 22.93372 25.14445 23.84235 27.25468 39.29402 50
data.table 21.23984 22.29121 27.28266 24.05491 26.25416 98.35812 50
quantmod 14.21228 16.71663 19.54709 17.19368 19.38106 102.56189 50
但是,对于N = 1e6,我观察到data.table 的性能差异很大:
Unit: milliseconds
expr min lq mean median uq max neval
xts 296.8969 380.7494 408.7696 397.4292 431.1306 759.7227 50
data.table 1562.3613 1637.8787 1669.8513 1651.4729 1688.2312 1969.4942 50
quantmod 144.1901 244.2427 278.7676 268.4302 331.4777 418.7951 50
我很好奇是什么推动了这个结果,特别是因为data.table 通常在N 上表现出色。当然,dtfun 可能写得不好(我非常感谢任何代码改进),但我使用其他方法获得了类似的结果,包括 EOM 日期的自联接和每日回报的cumprod。
xts 和/或 quantmod 是否受益于任何可大规模提高其性能的内部 rcpp 或 eqv 调用?最后,如果您知道任何其他有竞争力的大型 TS 独立解决方案(base?、dplyr?),我会全力以赴。
【问题讨论】:
-
瓶颈 AFAIK 正在舍入日期。
as.yearmon使用POSIXlt,data.table 的mday使用 POSIXlt。 POSIXlt 效率低下。 data.table 的round.IDate使用慢速ISOdate。有待改进的请求:github.com/Rdatatable/data.table/issues/4335 -
@jangorecki 谢谢!在改进日期处理之前有什么临时解决方法的想法吗?我尝试使用
.(year(V1), month(V1))而不是as.yearmon(V1)进行键控,结果相似。 -
查看
year和month的来源就明白了 -
@jangorecki 欢呼。
-
@JDG 你能用你从 Twitter 上的讨论中学到的东西自己回答这个问题吗?
标签: r time-series data.table xts quantmod