Rcpp中的滚动总和功能

时间:2018-12-03 00:47:41

标签: r data.table rcpp zoo

目前,我正在处理一个大型数据框,并且必须为多个变量创建多个长度的滚动总和。我有一种通过data.table的有效方法,但是通过一个变量(每个变量大约需要50分钟)来运行会花费很多时间。

我花了一些时间来改善脚本以加快速度,但想法不多了。我没有C ++的经验,但认为Rcpp包可能是一个选择。我本人已对其进行了调查,但未能提出任何可用的方法。

这是我的data.table脚本的一个变量

df_td <- setDT(df_1, key=c("Match","Name"))[,by=.(Match, Name), paste0("Period_", 1:10) 
                                        := mclapply((1:10)*600, function(x) rollsumr(Dist, x, fill = NA))][]

我使用了parallel::mclapply可以起到帮助作用,但是仍然需要花费大量时间才能工作。

> dput(head(df_1, 20))
structure(list(Match = c("Bath_A", "Bath_A", "Bath_A", "Bath_A", 
"Bath_A", "Bath_A", "Bath_A", "Bath_A", "Bath_A", "Bath_A", "Bath_A", 
"Bath_A", "Bath_A", "Bath_A", "Bath_A", "Bath_A", "Bath_A", "Bath_A", 
"Bath_A", "Bath_A"), Name = c("Jono Lance", "Jono Lance", "Jono     Lance", 
"Jono Lance", "Jono Lance", "Jono Lance", "Jono Lance", "Jono Lance", 
"Jono Lance", "Jono Lance", "Jono Lance", "Jono Lance", "Jono Lance", 
"Jono Lance", "Jono Lance", "Jono Lance", "Jono Lance", "Jono Lance", 
"Jono Lance", "Jono Lance"), Dist = c(0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), Dist_HS = c(0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), Dist_SD = c(0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)), row.names =    c(NA, 
-20L), class = c("tbl_df", "tbl", "data.frame"))

> str(df_1)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame':   26533771 obs. of  5     variables:
$ Match  : chr  "Bath_A" "Bath_A" "Bath_A" "Bath_A" ...
$ Name   : chr  "Jono Lance" "Jono Lance" "Jono Lance" "Jono Lance"   ...
$ Dist   : num  0 0 0 0 0 0 0 0 0 0 ...
$ Dist_HS: num  0 0 0 0 0 0 0 0 0 0 ...
$ Dist_SD: num  0 0 0 0 0 0 0 0 0 0 ...

任何可以加快速度的建议将不胜感激

2 个答案:

答案 0 :(得分:1)

由于存在重叠的总和,因此您可以重复使用先前迭代中的总和。这是使用shift

的一种可能方法
library(RcppRoll)
DT[, Period_1 := roll_sumr(Dist, 600L, fill=NA), by=.(ID)]
for (n in 2L:10L) {
    DT[, paste0("Period_", n) := {
            x <- get(paste0("Period_", n-1L))
            shift(x, 600L) + Period_1
        },
        by=.(ID)]
}

使用Reduce替换循环:

library(RcppRoll)
DT[, Period_1 := roll_sumr(Dist, 600L, fill=NA), by=.(ID)]
DT[, paste0("Period_", 1L:10L) :=
    Reduce(function(x, y) x + y, shift(Period_1, (1L:9L)*600L), Period_1, accum=TRUE),
    by=.(ID)]

数据:

library(data.table)
set.seed(0L)
nsampl <- 6003
nIDs <- 1
DT <- data.table(ID=rep(1:nIDs, each=nsampl), 
    Dist=rnorm(nIDs*nsampl, 1000, 100))

答案 1 :(得分:0)

我可能已经找到问题here的解决方案。通过从Rcpp

添加以下功能
cppFunction('
NumericVector run_sum_v2(NumericVector x, int n) {

        int sz = x.size();

        NumericVector res(sz);

        // sum the values from the beginning of the vector to n 
        res[n-1] = std::accumulate(x.begin(), x.end()-sz+n, 0.0);

        // loop through the rest of the vector
        for(int i = n; i < sz; i++) {
        res[i] = res[i-1] + x[i] - x[i-n];
        }

        // pad the first n-1 elements with NA
        std::fill(res.begin(), res.end()-sz+n-1, NA_REAL);

        return res;
        }
        ')

run_sum_v2可以代替data.table放在我的zoo:rollsumr行中,并且似乎更快(<1分钟)。需要对最终数据进行一些检查,但到目前为止看起来很有希望。

花费了2个小时以上的脚本,时间少于20秒,所以我对这种方法感到满意,除非它有任何问题?