4

次のようなデータフレームがあります。

   productid   ordernum   
   p1          10
   p2          20
   p3          30 
   p4          5
   p5          20
   p6          8

groupid と呼ばれる別の列を追加したいと思います。製品を順番にグループ化し、 sum(ordernum) が 30 に達すると、新しいグループ ID を割り当てます。たとえば、結果は次のようになります。

 productid   ordernum  groupid   
   p1          10        1
   p2          20        1 
   p3          30        2
   p4          5         3
   p5          20        3 
   p6          8         3

ループすることは非常に簡単ですが、ループせずにこれを達成するにはどうすればよいですか?

4

1 に答える 1

4

c++を使用して短い記述forループはどうですかRcpp。この小さな関数はnumericベクトル、つまりordernum列とthreshold引数 (新しい ID を開始する累積合計) を取り、入力ベクトルと等しい長さの ID のベクトルを返します。forのループであるため、比較的速く実行する必要がありますc++。以下のコード スニペットRcppは、まだインストールされていない場合にインストールされ、使用できるように関数をコンパイルします。コピーしてRに貼り付けるだけです...

if( !require(Rcpp) ) install.packages("Rcpp"); require(Rcpp)
Rcpp::cppFunction( ' NumericVector grpid( NumericVector x , int threshold ){
  int n = x.size();
  NumericVector out(n);
  int tot = 0;
  int id = 1;
  for( int i = 0; i < n; ++i){
    tot += x[i];
    out[i] = id;
    if( tot >= threshold ){
      id += 1;
      tot = 0;
    }
  }
  return out;
}')

次に、関数を使用するには、関連する引数を指定して、他の R 関数と同様に使用します。

df$groupid <- grpid( df$ordernum , 30 )
#  productid ordernum groupid
#1        p1       10       1
#2        p2       20       1
#3        p3       30       2
#4        p4        5       3
#5        p5       20       3
#6        p6        8       3

ベンチマーク比較

OPは、ベースR forループに対してRcppループをベンチマークするように私に依頼しました。これがコードと結果です。100,000 個の製品 ID のベクトルで約 400 倍の速度向上:

set.seed(1)
x <- sample(30,1e5,repl=T)
for.loop <- quote({
    tot <- 0 
    id <- 1
    out <- numeric(length(x))
    for( i in 1:length(x) ){
        tot <- tot + x[i]
        out[i] <- id
        if( tot >= 30 ){
            tot <- 0
            id <- id + 1
        }
    }
})

rcpp.loop <- quote( out <- grpid(x,30))

require( microbenchmark )
print( bm , unit = "relative" , digits = 2 , "median" )
Unit: relative
            expr min  lq median  uq max neval
 eval(rcpp.loop)   1   1      1   1   1    50
  eval(for.loop) 533 462    442 428 325    50
于 2013-08-30T12:07:29.233 に答える