在日期的矢量上使用sapply:函数非常慢。为什么?

问题描述:

我有一个非常简单的函数,它接受一个POSIXct日期中提取年,并减去1如果日期是6月1日在日期的矢量上使用sapply:函数非常慢。为什么?

library(lubridate) 
DetermineWaterYear <- function(date, 
           return.interval=FALSE){ 
    wy <- year(date) + ifelse(month(date)>=6, 0, -1) 
    if(return.interval==FALSE){ 
    return(wy) 
    } else { 
    interval <- interval(ymd(cat(wy),'06-01', sep=''), ymd(cat(wy+1),'05-31', sep='')) 
    return(interval) 
    } 
} 

之前,当我尝试使用sapply(),以在执行此功能约190k日期的向量,它需要永远。

sapply(temp$date, DetermineWaterYear) 

此外,我主频也从长度10000矢量的子集进行sapply至190000使用下面的代码:

tempdates <- rep(ymd('1956-01-01'), 190000) 


index <- seq(10000,190000,10000) 
for(i in 1:length(index)){ 
    times[i] <- system.time(sapply(tempdates[1:index[i]], DetermineWaterYear))[3] 
} 

疯狂的事情是,随着日期的载体变长,每个记录的处理时间大大增加......处理190k日期所需的时间是10k个日期所需的时间的238倍。我有足够的内存可用。

Plot of # of records vs. processing time

这是为什么表现这么慢?我怎样才能优化它?

+1

这似乎是一个可怕的很多重型机械的东西这可以通过在日期的字符表示上假设一个单独的'ifelse'语句(假设你的日期都是干净的,格式良好的日期)来完成(可能几乎立即)。 – joran

+1

为什么使用sapply?我没有检查,但你的功能似乎是矢量化的。 – Roland

+0

@Roland Doh,它是矢量化的,我可以做DetermineWaterYear(temp $日期)来得到我的结果,速度要快得多。尽管如此,仍然很好奇为什么萨克利会如此沉迷。 – user278411

正如在评论中指出的那样,将日期向量直接传递给函数会更快。此外,ifelse有一吨的开销,所以用ifelse(month(date)>=6, 0, -1)代替floor((x/5.6) - (x^2)*0.001) - 1L会快得多。

DetermineWaterYearNew <- function(date, return.interval=FALSE){ 
    x <- month(date) 
    wy <- year(date) + floor((x/5.6) - (x^2)*0.001) - 1L 
    if(return.interval==FALSE){ 
     return(wy) 
    } else { 
     interval <- interval(ymd(cat(wy),'06-01', sep=''), ymd(cat(wy+1),'05-31', sep='')) 
     return(interval) 
    } 
} 

这里有一些基准:

microbenchmark(NewVectorized=DetermineWaterYearNew(tempdates[1:1000]), 
       OldVectorized=DetermineWaterYear(tempdates[1:1000]), 
       NonVectorized=sapply(tempdates[1:1000],DetermineWaterYear)) 
Unit: microseconds 
     expr  min   lq  mean  median   uq  max neval 
NewVectorized 341.954 364.1215 418.7311 395.7300 460.7955 602.627 100 
OldVectorized 417.077 437.3970 496.0585 462.8485 545.1555 802.954 100 
NonVectorized 42601.719 45148.3070 46452.6843 45902.4100 47341.2415 62898.476 100 

只有比较约会的全域向量化的解决方案,我们有:

microbenchmark(NewVectorized=DetermineWaterYearNew(tempdates[1:190000]), 
       OldVectorized=DetermineWaterYear(tempdates[1:190000])) 
Unit: milliseconds 
     expr  min  lq  mean median  uq  max neval 
NewVectorized 26.30660 27.26575 28.97715 27.84169 29.19391 102.1697 100 
OldVectorized 38.98637 40.78153 44.07461 42.55287 43.77947 114.9616 100