在r中制作这个ifelse循环的更快捷的方法

时间:2014-05-02 16:34:02

标签: r if-statement for-loop

我在R [300000,45]中有一个较大的数据框。我想添加一个TRUE / FALSE列(或创建一个向量),如果另一列的值不同于上面的值(i-1),则分配TRUE,如果它们相同则为FALSE。基本的R代码是:

etS$ar1TF <- NA
mode(etS$ar1TF) <- 'logical'
etS$ar1TF[1] <- TRUE
for(i in 2:length(etS$ar1TF)) {
  if(etS$siteYear[i] == etS$siteYear[i-1]) {
    etS$ar1TF[i] <- FALSE
  } else {
    etS$ar1TF[i] <- TRUE
  }
}

然而,这将是非常缓慢和低效的。有没有更好的方法来使用现有的功能或矢量化来快速有效地完成这项工作?我不确定while()语句是否会更有效率。我想我可以从将所有内容分配为TRUE开始,然后在for循环中使用if语句并删除else语句,但这确实不是更好。在这种情况下,我不确定apply函数是更快还是更有效,因为已经分配了大小和类型。

2 个答案:

答案 0 :(得分:3)

利用矢量化。像下面这样的东西可以解决这个问题:

ar1TF <- logical(length(siteYear))
ar1TF[-1] <- (siteYear[-1] != siteYear[-length(siteYear)])
ar1TF[1] <- NA

etS$ar1TF <- ar1TF # to add the column to the data.frame

编辑:似乎diff解决方案可能会更快一点:

x <- sample(1:3, 100000, replace=TRUE)
library('microbenchmark')
microbenchmark({
   y1 <- logical(length(x))
   y1[-1] <- (x[-1] != x[-length(x)])
   y1[1] <- NA
},{
   y2 <- diff(x)
   y2 <- c(NA, y2 != 0)
})

## Unit: microseconds
## expr        min       lq    median       uq      max neval
## [!=]   1062.651 1070.690 1088.1935 1169.500 2367.582   100
## [diff]  811.121  821.443  844.3575  892.967 2244.022   100

答案 1 :(得分:2)

您可以使用diff执行差异:

vec = sample(1:10, 100, replace = TRUE)
diff(vec) == 0
 [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[13] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[25] FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[37] FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE
[49] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE
[61] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE
[73] FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[85] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[97] FALSE FALSE FALSE

diff的标准设置使用滞后1,这正是您所需要的。要将其添加到data.frame,您需要附加NA

df$new_col = c(NA, diff(vec) == 0)

一些基本时间表明,这对于较大的向量来说非常快:

> system.time(dum <- diff(sample(1:10, 10e3, replace = TRUE)) == 0)
   user  system elapsed 
  0.001   0.000   0.001 
> system.time(dum <- diff(sample(1:10, 10e5, replace = TRUE)) == 0)
   user  system elapsed 
  0.189   0.012   0.202 
> system.time(dum <- diff(sample(1:10, 10e7, replace = TRUE)) == 0)
   user  system elapsed 
  6.810   1.908  10.376 

因此,使用您的datasize,处理时间应小于一秒。请注意,这些时间包括创建测试数据集,因此实际差异几乎快两倍。

与基于for循环的解决方案进行直接比较会显示速度差异:

diff_for_loop = function(vec) {
    result_vec = vec
    for(i in seq_along(vec)[-1]) {
      if(vec[i] == vec[i-1]) {
        result_vec <- FALSE
      } else {
        result_vec <- TRUE
      }
    }
    return(result_vec)
}
vec = sample(1:10, 10e5, replace = TRUE)
system.time(dum_for_loop <- diff_for_loop(vec))
#   user  system elapsed 
#  1.220   0.008   1.232 
system.time(dum_diff <- diff(vec) == 0)
#   user  system elapsed 
#  0.051   0.005   0.056 

这使基于diff的解决方案的速度提高了22倍。