不要改变 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)
)
*/