2017-02-12 91 views
1

使用R中的向量時,diff函數會計算每個值與前一個值之間的差異。從?diffR diff函數爲什麼很慢?

如果x是長度ndifferences = 1的向量,然後計算結果等於所述連續差x[(1+lag):n] - x[1:(n-lag)]

然而,當我測試的執行時間diff函數的VS其理論表達式(使用microbenchmark函數的microbenchmark包),diff函數較慢。這裏是我的代碼:

library(microbenchmark) 

mb.diff1 <- function(n, seed){ 
    set.seed(seed) 
    vec <- runif(n) 
    out <- diff(vec) 
    return(out) 
} 

mb.diff2 <- function(n, seed){ 
    set.seed(seed) 
    vec <- runif(n) 
    out <- vec[2:n]-vec[1:(n-1)] 
    return(out) 
} 

times.diff1 <- c() 
times.diff2 <- c() 
vec.sizes <- c(1e1, 1e2, 1e3, 1e4) 
for (n in vec.sizes){ 
    bench <- microbenchmark(
    mb.diff1(n,1), 
    mb.diff2(n,1)) 
    times.median <- aggregate(
    bench$time, 
    by = list(bench$expr), 
    FUN = median) 
    times.diff1 <- c(times.diff1, times.median[1,2]) 
    times.diff2 <- c(times.diff2, times.median[2,2]) 
} 

perf.ratio <- times.diff1/times.diff2 
names(perf.ratio) <- vec.sizes 
print(perf.ratio) 

我完成了1E4的vec.sizes,所以excution時間爲你們並不需要太長時間,但我讓他們去,直到1E7。你可以看到結果這裏:

enter image description here

正如你所看到的,diff功能是所有矢量大小慢。商數趨於減少,因爲在兩種情況下,執行時間似乎都是向量大小的線性函數,所以我們不能說隨着n的增加diff表現更好。因此,這裏提出的問題:

  1. (顯而易見的問題)我在測量執行時間時在代碼中做錯了什麼?
  2. diff函數的原因是什麼可能比他們的理論表達式慢?
  3. 你知道計算差異向量的最有效方法嗎?比x[(1+lag):n] - x[1:(n-lag)]

我在Linux中使用R 3.1.2。

非常感謝您提前。

+2

R-3.1.2是2歲以上。你的時間安排包括產生一個隨機向量,所以你並不是孤立你所要求的差異。使用只調用'diff'和'vec []'的簡化函數,在我運行R-3.3.2的Ubuntu 14.04機器上,'diff'更快。 'diff'也是通用的,所以方法調度有一定的成本。直接調用'diff.default'(通常不推薦)速度更快。 –

+1

另外'diff.default'將其輸入轉換爲矩陣並進行一些檢查。 https://github.com/wch/r-source/blob/trunk/src/library/base/R/diff.R –

+0

感謝@JoshuaUlrich,我將Ubuntu更新至16.04並安裝了R 3.3.2。我還調用runif函數以外的函數,以便可以衡量diff函數的實際執行時間。對於所有這些,Whit比1e4更好。我認爲小尺寸的vec []更好的性能是由於檢查。非常感謝你。 –

回答

0

以下是一些代碼,用於幫助擴展和說明我在評論中提出的觀點。

library(microbenchmark) 

mb.diff2 <- compiler::cmpfun(function(vec) { 
    n <- length(vec) 
    vec[2:n]-vec[1:(n-1L)] 
}) 

times.diff1 <- c() 
times.diff2 <- c() 
times.diff3 <- c() 
vec.sizes <- c(1e1, 1e2, 1e3, 1e4, 1e5) 

for (n in vec.sizes) { 
    set.seed(21) 
    vec <- runif(n) 
    bench <- microbenchmark(diff(vec), mb.diff2(vec), diff.default(vec)) 
    times.median <- aggregate(bench$time, by = list(bench$expr), FUN = median) 
    times.diff1 <- c(times.diff1, times.median[1,2]) 
    times.diff2 <- c(times.diff2, times.median[2,2]) 
    times.diff3 <- c(times.diff3, times.median[3,2]) 
} 

setNames(times.diff1/times.diff2, vec.sizes) 
setNames(times.diff1/times.diff3, vec.sizes) 

首先,你會注意到我編譯了mb.diff2函數。這是因爲diffdiff.default是字節編譯的。我還將n的計算放在mb.diff2之內,因爲計算矢量長度應該是測量函數調用的一部分。

這裏有定時的結果,伴隨着我的sessionInfo()

R> setNames(times.diff1/times.diff2, vec.sizes) 
     10  100  1000  10000  1e+05 
3.5781536 2.3330988 1.2488135 0.9011312 0.9660411 
R> setNames(times.diff1/times.diff3, vec.sizes) 
     10  100  1000  10000  1e+05 
1.5945010 1.4609283 1.1021190 1.0034623 0.9987618 
R> sessionInfo() 
R version 3.3.2 (2016-10-31) 
Platform: x86_64-pc-linux-gnu (64-bit) 
Running under: Ubuntu 16.04.1 LTS 

locale: 
[1] LC_CTYPE=en_US.UTF-8  LC_NUMERIC=C    
[3] LC_TIME=en_US.UTF-8  LC_COLLATE=en_US.UTF-8  
[5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 
[7] LC_PAPER=en_US.UTF-8  LC_NAME=C     
[9] LC_ADDRESS=C    LC_TELEPHONE=C    
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C  

attached base packages: 
[1] stats  graphics grDevices utils  datasets methods base  

other attached packages: 
[1] microbenchmark_1.4-2 

loaded via a namespace (and not attached): 
[1] Rcpp_0.12.9  digest_0.6.8  MASS_7.3-45  grid_3.3.2  
[5] plyr_1.8.4  gtable_0.1.2  magrittr_1.5  scales_0.3.0  
[9] ggplot2_1.0.1 stringi_0.4-1 reshape2_1.4.1 proto_0.3-10  
[13] tools_3.3.2  stringr_1.0.0 munsell_0.4.2 compiler_3.3.2 
[17] colorspace_1.2-6