Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么zoo::rollmean比简单的Rcpp实现慢?_R_Zoo_Moving Average - Fatal编程技术网

为什么zoo::rollmean比简单的Rcpp实现慢?

为什么zoo::rollmean比简单的Rcpp实现慢?,r,zoo,moving-average,R,Zoo,Moving Average,zoo::rollmean是一个有用的函数,返回时间序列的滚动平均值;对于长度n和窗口大小k的向量x,它返回向量c(平均值(x[1:k])、平均值(x[2:(k+1)]、…、平均值(x[(n-k+1):n]) 我注意到,对于我正在开发的一些代码,它似乎运行得很慢,因此我使用Rcpp包和一个简单的for循环编写了自己的版本: library(Rcpp) cppFunction("NumericVector rmRcpp(NumericVector dat, const int window) {

zoo::rollmean
是一个有用的函数,返回时间序列的滚动平均值;对于长度
n
和窗口大小
k
的向量
x
,它返回向量
c(平均值(x[1:k])、平均值(x[2:(k+1)]、…、平均值(x[(n-k+1):n])

我注意到,对于我正在开发的一些代码,它似乎运行得很慢,因此我使用Rcpp包和一个简单的for循环编写了自己的版本:

library(Rcpp)
cppFunction("NumericVector rmRcpp(NumericVector dat, const int window) {
  const int n = dat.size();
  NumericVector ret(n-window+1);
  double summed = 0.0;
  for (int i=0; i < window; ++i) {
    summed += dat[i];
  }
  ret[0] = summed / window;
  for (int i=window; i < n; ++i) {
    summed += dat[i] - dat[i-window];
    ret[i-window+1] = summed / window;
  }
  return ret;
}")
加速比甚至适用于更大的矢量:

# Time series with 5 million elements
set.seed(144)
y <- rnorm(5000000)
x <- 1:5000000
library(zoo)
zoo.dat <- zoo(y, x)

# Make sure our function works
all.equal(as.numeric(rollmean(zoo.dat, 3)), rmRcpp(y, 3))
# [1] TRUE

# Benchmark
library(microbenchmark)
microbenchmark(rollmean(zoo.dat, 3), rmRcpp(y, 3), times=10)
# Unit: milliseconds
#                  expr        min         lq       mean     median         uq        max
#  rollmean(zoo.dat, 3) 2825.01622 3090.84353 3191.87945 3206.00357 3318.98129 3616.14047
#          rmRcpp(y, 3)   31.03014   39.13862   42.67216   41.55567   46.35191   53.01875
#包含500万个元素的时间序列
种子集(144)
y翻看一下,似乎
rollmean.*
方法都是在R中实现的


而C++实现了一个。打包的R代码可能还会做一些检查等等。所以,你可能不那么惊讶了,你可以打败它。

< P>谢谢@ DrkdDelBueTelt,指出在这个问题中所做的比较不是最公平的,因为我正在把C++代码和纯R代码进行比较。下面是一个简单的BaseR实现(没有来自zoo包的所有检查);这与
zoo::rollmean
实现滚动平均值核心计算的方式非常相似:

baseR.rollmean <- function(dat, window) {
  n <- length(dat)
  y <- dat[window:n] - dat[c(1, 1:(n-window))]
  y[1] <- sum(dat[1:window])
  return(cumsum(y) / window)
}
为了探究为什么我们在使用base R时会看到10倍的加速,我使用了Hadley的lineprof工具,从
zoo
软件包中获取源代码,在需要的地方:

lineprof(rollmean.zoo(zoo.dat, 3))
#     time  alloc release dups ref                  src
# 1  0.001  0.954       0   26 #27 rollmean.zoo/unclass
# 2  0.001  0.954       0    0 #28 rollmean.zoo/:      
# 3  0.002  0.954       0    1 #28 rollmean.zoo        
# 4  0.001  1.431       0    0 #28 rollmean.zoo/seq_len
# 5  0.001  0.000       0    0 #28 rollmean.zoo/c      
# 6  0.006  2.386       0    1 #28 rollmean.zoo        
# 7  0.002  0.954       0    2 #31 rollmean.zoo/cumsum 
# 8  0.001  0.000       0    0 #31 rollmean.zoo//      
# 9  0.005  1.912       0    1 #33 rollmean.zoo        
# 10 0.013  2.898       0   14 #33 rollmean.zoo/[<-    
# 11 0.299 28.941       0  127 #34 rollmean.zoo/na.fill
几乎所有的时间都花在对
zoo
对象进行子集设置上:

lineprof("[.zoo"(zoo.dat, 2:999999))
#    time  alloc release dups          ref            src
# 1 0.004  0.004       0    0 character(0)               
# 2 0.002  1.922       0    4           #4 [.zoo/coredata
# 3 0.038 11.082       0   29          #19 [.zoo/zoo     
# 4 0.004  0.000       0    1          #28 [.zoo 
几乎所有的时间都花在使用
zoo
函数构造新的zoo对象上:

# Time series with 1000 elements
set.seed(144)
y <- rnorm(1000)
x <- 1:1000
library(zoo)
zoo.dat <- zoo(y, x)

# Make sure our function works
all.equal(as.numeric(rollmean(zoo.dat, 3)), rmRcpp(y, 3))
# [1] TRUE

# Benchmark
library(microbenchmark)
microbenchmark(rollmean(zoo.dat, 3), rmRcpp(y, 3))
# Unit: microseconds
#                  expr     min       lq       mean    median        uq       max neval
#  rollmean(zoo.dat, 3) 685.494 904.7525 1776.88666 1229.2475 1744.0720 15724.321   100
#          rmRcpp(y, 3)   6.638  12.5865   46.41735   19.7245   27.4715  2418.709   100
lineprof(zoo(y[2:999999], 2:999999))
#    time alloc release dups                ref        src
# 1 0.021 4.395       0    8 c("zoo", "unique") zoo/unique
# 2 0.012 0.477       0    8  c("zoo", "ORDER") zoo/ORDER 
# 3 0.001 0.477       0    1              "zoo" zoo       
# 4 0.001 0.954       0    0      c("zoo", ":") zoo/:     
# 5 0.015 3.341       0    5              "zoo" zoo      
设置新zoo对象所需的各种操作(例如,确定唯一时间点并对其排序)


总之,
zoo
包似乎通过构造新的zoo对象而不是使用当前zoo对象的内部结构,为其滚动平均操作增加了大量开销;与基本R实现相比,这造成了10倍的减速,与Rcpp实现相比,造成了100倍的减速。

RcppRoll
包提供了更快的
zoo::roll
s实现。
lineprof("[.zoo"(zoo.dat, 2:999999))
#    time  alloc release dups          ref            src
# 1 0.004  0.004       0    0 character(0)               
# 2 0.002  1.922       0    4           #4 [.zoo/coredata
# 3 0.038 11.082       0   29          #19 [.zoo/zoo     
# 4 0.004  0.000       0    1          #28 [.zoo 
lineprof(zoo(y[2:999999], 2:999999))
#    time alloc release dups                ref        src
# 1 0.021 4.395       0    8 c("zoo", "unique") zoo/unique
# 2 0.012 0.477       0    8  c("zoo", "ORDER") zoo/ORDER 
# 3 0.001 0.477       0    1              "zoo" zoo       
# 4 0.001 0.954       0    0      c("zoo", ":") zoo/:     
# 5 0.015 3.341       0    5              "zoo" zoo