始める無料で始める

Rcpp ベクトルのサイズは変更しないでください

Rcpp のベクトルクラスは、R のベクトルをほぼそのまま薄く包んだラッパーとして設計されています。つまり、サイズを増減させると、その都度、適切なサイズの新しいベクトルを作成し、関連データをコピーする必要があります。これは非常に遅い処理なので、避けるべきです。

可能であれば、まず最終的なベクトルのサイズを計算してから、そのサイズでメモリを確保するようにコードを組み立ててください。

ここでは、R の x[x > 0] と同等の、ベクトルから正の値だけを選ぶ例を見てみましょう。事前に正の数がいくつあるか分からないため、要素数 0 のベクトルから始めて、見つけるたびに値を末尾に追加したくなるかもしれません。ここでの push_back() は値を追加する関数です。

NumericVector bad_select_positive_values_cpp(NumericVector x) {
  NumericVector positive_x(0);
  for(int i = 0; i < x.size(); i++) {
    if(x[i] > 0) {
      positive_x.push_back(x[i]);
    }
  }
  return positive_x;
}

しかし、この関数はそのたびに新しいベクトルを作ってデータをコピーするため、遅くなってしまいます。もっと良い方法を考えてみましょう!

この演習はコースの一部です

Rcpp で R コードを最適化する

コースを見る

演習の手順

  • より効率的な関数 good_select_positive_values_cpp() の定義を完成させ、正の数だけを選択してください。
    • 最初の for ループでは、x の i 番目の要素が 0 より大きければ、n_positive_elements に 1 を加えます。
    • そのループの後、サイズが n_positive_elements の数値ベクトル positive_x を確保します。
    • 2 つ目の for ループでも、x の i 番目の要素が 0 より大きいかを再度確認します。
    • 条件を満たしたら、positive_x の j 番目の要素を x の i 番目の要素に設定し、その後で j に 1 を加えます。
  • 比較のために bad_select_positive_values_cpp() がワークスペースに用意されています。コンソール出力を確認して、実行時間のベンチマーク差を見てみましょう。

実践的なインタラクティブ演習

このサンプルコードを完成させて、この演習に挑戦してみましょう。

#include 
using namespace Rcpp;

// [[Rcpp::export]]
NumericVector good_select_positive_values_cpp(NumericVector x) {
  int n_elements = x.size();
  int n_positive_elements = 0;
  
  // Calculate the size of the output
  for(int i = 0; i < n_elements; i++) {
    // If the ith element of x is positive
    if(___) {
      // Add 1 to n_positive_elements
      ___;
    }
  }
  
  // Allocate a vector of size n_positive_elements
  ___;
  
  // Fill the vector
  int j = 0;
  for(int i = 0; i < n_elements; i++) {
    // If the ith element of x is positive
    if(___) {
      // Set the jth element of positive_x to the ith element of x
      ___;
      // Add 1 to j
      ___;
    }
  }
  return positive_x;
}

/*** R
set.seed(42)
x <- rnorm(1e4)
# Does it give the same answer as R?
all.equal(good_select_positive_values_cpp(x), x[x > 0])
# Which is faster?
microbenchmark(
  bad_cpp = bad_select_positive_values_cpp(x),
  good_cpp = good_select_positive_values_cpp(x)
)
*/
コードを編集して実行