R - 向量化条件替换

时间:2012-07-07 18:39:52

标签: r for-loop logic vectorization conditional-statements

嗨我正在尝试操作一个数字列表,我想在没有for循环的情况下这样做,在R中使用快速本机操作。操作的伪代码是:

  

默认情况下,起始总数为100(对于零内的每个块)

     

从第一个零到下一个零,累计总数下降超过2%的时刻将所有后续数字替换为零。

     

在零之内完成所有数字块

     

每次累计总和重置为100

例如,如果以下是我的数据:

d <- c(0,0,0,1,3,4,5,-1,2,3,-5,8,0,0,-2,-3,3,5,0,0,0,-1,-1,-1,-1);

结果将是:

0 0 0 1 3 4 5 -1 2 3 -5 0 0 0 -2 -3 0 0 0 0 0 -1 -1 -1 0

目前我有一个带for循环的实现,但由于我的向量很长,性能很差。

提前致谢。

这是一个正在运行的示例代码:

d <- c(0,0,0,1,3,4,5,-1,2,3,-5,8,0,0,-2,-3,3,5,0,0,0,-1,-1,-1,-1);
ans <- d;
running_total <- 100;
count <- 1;
max <- 100;
toggle <- FALSE;
processing <- FALSE;

for(i in d){
  if( i != 0 ){  
       processing <- TRUE; 
       if(toggle == TRUE){
          ans[count] = 0;  
       }
       else{
         running_total = running_total + i;

          if( running_total > max ){ max = running_total;}
          else if ( 0.98*max > running_total){
              toggle <- TRUE;  
          }
      }
   }

   if( i == 0 && processing == TRUE )
   { 
       running_total = 100; 
       max = 100;
       toggle <- FALSE;
   }
   count <- count + 1;
}
cat(ans)

1 个答案:

答案 0 :(得分:7)

我不确定如何将循环转换为矢量化操作。但是,有两个相当容易的选项可以实现大的性能改进。第一种是简单地将循环放入R函数,并使用compiler包进行预编译。第二个稍微复杂的选项是将R循环转换为c++循环,并使用Rcpp包将其链接到R函数。然后你调用一个R函数将它传递给快速的c++代码。我展示了这些选项和时间。我非常感谢Rcpp listserv的Alexandre Bujard的帮助,他帮我解决了一个我不理解的指针问题。

首先,这是您的R循环作为函数,foo.r

## Your R loop as a function
foo.r <- function(d) {
  ans <- d
  running_total <- 100
  count <- 1
  max <- 100
  toggle <- FALSE
  processing <- FALSE

  for(i in d){
    if(i != 0 ){
      processing <- TRUE
      if(toggle == TRUE){
        ans[count] <- 0
      } else {
        running_total = running_total + i;
        if (running_total > max) {
          max <- running_total
        } else if (0.98*max > running_total) {
          toggle <- TRUE
        }
      }
    }
    if(i == 0 && processing == TRUE) {
      running_total <- 100
      max <- 100
      toggle <- FALSE
    }
    count <- count + 1
  }
  return(ans)
}

现在我们可以加载compiler包并编译该函数并将其命名为foo.rcomp

## load compiler package and compile your R loop
require(compiler)
foo.rcomp <- cmpfun(foo.r)

这就是编译路线所需的全部内容。它全是R,显然非常容易。对于c++方法,我们使用Rcpp包以及inline包,它允许我们“内联”c++代码。也就是说,我们不必创建源文件并对其进行编译,我们只需将其包含在R代码中,并为我们处理编译。

## load Rcpp package and inline for ease of linking
require(Rcpp)
require(inline)

## Rcpp version
src <- '
  const NumericVector xx(x);
  int n = xx.size();
  NumericVector res = clone(xx);
  int toggle = 0;
  int processing = 0;
  int tot = 100;
  int max = 100;

  typedef NumericVector::iterator vec_iterator;
  vec_iterator ixx = xx.begin();
  vec_iterator ires = res.begin();
  for (int i = 0; i < n; i++) {
    if (ixx[i] != 0) {
      processing = 1;
      if (toggle == 1) {
        ires[i] = 0;
      } else {
        tot += ixx[i];
        if (tot > max) {
          max = tot;
        } else if (.98 * max > tot) {
            toggle = 1;
          }
      }
    }

   if (ixx[i] == 0 && processing == 1) {
     tot = 100;
     max = 100;
     toggle = 0;
   }
  }
  return res;
'

foo.rcpp <- cxxfunction(signature(x = "numeric"), src, plugin = "Rcpp")

现在我们可以测试我们是否得到了预期的结果:

## demonstrate equivalence
d <- c(0,0,0,1,3,4,5,-1,2,3,-5,8,0,0,-2,-3,3,5,0,0,0,-1,-1,-1,-1)
all.equal(foo.r(d), foo.rcpp(d))

最后,通过重复10e4次来创建更大版本的d。然后,我们可以运行三个不同的函数,纯R代码,已编译的R代码以及与R代码相关联的c++函数。

## make larger vector to test performance
dbig <- rep(d, 10^5)

system.time(res.r <- foo.r(dbig))
system.time(res.rcomp <- foo.rcomp(dbig))
system.time(res.rcpp <- foo.rcpp(dbig))

在我的系统上,给出:

> system.time(res.r <- foo.r(dbig))
   user  system elapsed 
  12.55    0.02   12.61 
> system.time(res.rcomp <- foo.rcomp(dbig))
   user  system elapsed 
   2.17    0.01    2.19 
> system.time(res.rcpp <- foo.rcpp(dbig))
   user  system elapsed 
   0.01    0.00    0.02 

编译后的R代码大约需要1/6的时间,未编译的R代码只花费2秒钟来操作250万的向量。即使编译后的c++代码只需0.02秒即可完成,R代码的速度要快几个数量级。除了初始设置之外,基本循环的语法在Rc++中几乎完全相同,因此您甚至不会失去清晰度。我怀疑,即使你的部分或全部循环都可以在R中进行矢量化,你也会感到痛苦地击败与R相关联的c++函数的性能。最后,仅用于证明:

> all.equal(res.r, res.rcomp)
[1] TRUE
> all.equal(res.r, res.rcpp)
[1] TRUE

不同的函数返回相同的结果。