多窗口范围计算:data.table与dplyr的比较

4

我正在进行股票回报的多窗口范围计算(即最大值和最小值)。 我已经使用dplyr创建了我的版本,但是许多人发布数据表格计算速度更快的基准测试。 我已经创建了使用data.table语法的版本,但速度比dplyr慢。 有人能帮我找到更好地利用data.table加快速度的方法吗?非常感谢。

library(Quandl)
library(tidyr)
library(dplyr)
library(data.table)
library(microbenchmark)

tickers <- c("GOOG/NASDAQ_AAPL", "GOOG/NASDAQ_MSFT", 
             "GOOG/NYSE_IBM", "GOOG/NASDAQ_GOOG") 

data <- Quandl(tickers,transformation = "rdiff")

returns <- gather(data, stock, value, -Date) %>%
    separate(stock, c("name", "field"), " - ") %>%
    filter(
       field == "Close"
    ) %>%
    select(
       - field
    )

returns_dt <- data.table(returns)

multi_window_range <- function(data) {
    result_1y <- data %>%
        filter(
            Date >= Sys.Date() - 365
        ) %>% 
        group_by(name) %>%
        summarise(
            max_1y = max(value, na.rm = TRUE),
            min_1y = min(value, na.rm = TRUE)
        )
    result_2y <- data %>%
        filter(
            Date >= Sys.Date() - 365 * 2
        ) %>%
        group_by(name) %>%
        summarise(
            max_2y = max(value, na.rm = TRUE),
           min_2y = min(value, na.rm = TRUE)
       )
    result_5y <- data %>%
        filter(
            Date >= Sys.Date() - 365 * 5
        ) %>%
        group_by(name) %>%
        summarise(
            max_5y = max(value, na.rm = TRUE),
            min_5y = min(value, na.rm = TRUE)
        )
    return(inner_join(inner_join(result_1y, result_2y, by = "name"), result_5y, by = "name"))
}

multi_window_range_dt <- function(data) {
    setkey(data, name)
    result_1y <- data[Date >= Sys.Date() - 365,
                      list(
                        max_1y = max(value, na.rm = TRUE),
                        min_1y = min(value, na.rm = TRUE)
                      ), by = "name"]
   result_2y <- data[Date >= Sys.Date() - 365 * 2,
                     list(
                        max_2y = max(value, na.rm = TRUE),
                        min_2y = min(value, na.rm = TRUE)
                     ), by = "name"]
   result_5y <- data[Date >= Sys.Date() - 365 * 5,
                     list(
                        max_5y = max(value, na.rm = TRUE),
                        min_5y = min(value, na.rm = TRUE)
                     ), by = "name"]
   return(result_1y[result_2y][result_5y])
}

microbenchmark(
    multi_window_range(returns),
    multi_window_range_dt(returns_dt)
)


Unit: milliseconds
                              expr      min       lq     mean   median       uq      max neval
       multi_window_range(returns) 6.341532 6.522303 6.915266 6.692666 6.922623 10.16709   100
 multi_window_range_dt(returns_dt) 7.537073 7.738516 8.066579 7.865968 8.073114 12.68021   100 
1个回答

1

试试这个:

multi_window_range_dt2 <- function(data) {
       data[, {
        rng1 <- range(value[Date > Sys.Date() - 365], na.rm = TRUE)
        rng2 <- range(value[Date > Sys.Date() - 2*365], na.rm = TRUE)
        rng5 <- range(value[Date > Sys.Date() - 5*365], na.rm = TRUE)
        list(max_1y = rng1[2], min_1y = rng1[1],
             max_2y = rng2[2], min_2y = rng2[1],
             max_5y = rng5[2], min_5y = rng5[1])
       }, by = "name"]
}

library(rbenchmark)
benchmark(multi_window_range(returns), multi_window_range_dt2(returns_dt))[1:4]

这在我的笔记本电脑上显示为:


                                test  replications elapsed relative
1        multi_window_range(returns)           100    2.39    1.189
2 multi_window_range_dt2(returns_dt)           100    2.01    1.000

这意味着multi_window_rangemulti_window_range_dt2花费的时间多了18.9%:

看起来这里的主要优势来自于使用范围而不是单独使用最小值和最大值。无论如何,这是一个好主意。 - kismsu
可能是由于最后消除了连接的原因。 - G. Grothendieck
啊,是的,节省的原因在于问题中仅将 by="name" 拆分一次而不是三次。公平的比较应该与 dplyr 仅分组一次相同。 - Daniel Krizian
是的,如果目的是比较dplyr和data.table,那么这并不是一个公平的比较,因为我们也可以将这些想法应用于dplyr,但问题如所述是如何加速data.table代码。 - G. Grothendieck
实际上,在dplyr中使用相同的技巧并不是很容易。summarise不接受range,而do也不是很快。 - kismsu
1
是的,但你可以通过以下方式实现部分功能:data %>% filter(Date > Sys.Date() - 5*365, !is.na(value)) %>% group_by(name) %>% summarize(max_1y = max(value[Date > Sys.Date() - 365]), min_1y = min(value[Date > Sys.Date() - 365]), max_2y = max(value[Date > Sys.Date() - 2*365]), min_2y = min(value[Date > Sys.Date() - 2*365]), max_5y = max(value), min_5y = min(value)) - G. Grothendieck

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