为什么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