开始使用免费开始使用

不要改变 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
    • 在第二个 for 循环中,再次检查 x 的第 i 个元素是否大于 0。
    • 若大于 0,则将 x 的第 i 个元素赋给 positive_x 的第 j 个元素,然后让 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)
)
*/
编辑并运行代码