2

こんにちは私は数値のリストを操作しようとしています。Rで高速ネイティブ操作を使用して、forループなしで操作したいと思います。操作の擬似コードは次のとおりです。

デフォルトでは、開始合計は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)
4

1 に答える 1

7

ループをベクトル化された操作に変換する方法がわかりません。ただし、パフォーマンスを大幅に向上させるためのかなり簡単なオプションが 2 つあります。1 つ目は、単純にループをR関数に入れ、compilerパッケージを使用してそれをプリコンパイルすることです。もう少し複雑な 2 番目のオプションは、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))

d最後に、これを 10e4 回繰り返して、より大きなバージョンの を作成します。次に、純粋なRコード、コンパイルされたRコード、およびコードにRリンクされた関数の 3 つの異なる関数を実行でき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 

コンパイルされたコードは、250 万のベクトルを操作するのに 2 秒しかかからないコンパイルされていないRコードの約 1/6 の時間を要します。Rこのコードは、完了するのにわずか 0.02 秒しかかからないc++コンパイル済みコードよりも桁違いに高速です。R初期設定を除けば、基本的なループの構文はほぼ同じであるRためc++、明確さを失うことさえありません。ループの一部またはすべてを でベクトル化できたとしても、 にリンクされRた関数のパフォーマンスを打ち負かすことに苦慮することになると思います。最後に、証明のために:Rc++

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

異なる関数は同じ結果を返します。

于 2012-07-08T00:22:29.097 に答える