No cambies el tamaño de los vectores de Rcpp
Las clases de vectores de Rcpp están diseñadas como envoltorios muy ligeros alrededor de los vectores de R. Esto implica que al hacerlos crecer o reducirlos se crea un nuevo vector del tamaño adecuado y se copian los datos relevantes. Esta tarea es muy lenta, por lo que conviene evitarla.
Si es posible, deberías estructurar tu código para calcular primero el tamaño final del vector y luego reservarlo con ese tamaño.
Veamos un ejemplo que selecciona los valores positivos de un vector, equivalente a x[x > 0] en R. Como no sabes de antemano cuántos números positivos habrá, puede resultar tentador empezar con un vector de longitud cero e ir añadiendo un valor cada vez que encuentres uno. Aquí, push_back() es una función que añade un valor al final.
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;
}
Por desgracia, esta función será lenta porque debe crear nuevos vectores y copiar los datos repetidamente. ¡A ver si puedes mejorarlo!
Este ejercicio forma parte del curso
Optimizar código de R con Rcpp
Instrucciones del ejercicio
- Completa la definición de una función más eficiente,
good_select_positive_values_cpp(), para seleccionar los números positivos.- En el primer bucle
for, si el elemento i-ésimo dexes mayor que cero, suma uno an_positive_elements. - Después de ese bucle
for, reserva un vector numérico,positive_x, de tamañon_positive_elements. - En el segundo bucle
for, vuelve a comprobar si el elemento i-ésimo dexes mayor que cero. - Cuando lo sea, asigna el elemento j-ésimo de
positive_xal elemento i-ésimo dexy luego suma uno aj.
- En el primer bucle
bad_select_positive_values_cpp()está disponible en tu espacio de trabajo para comparar. Examina la salida de la consola para ver la diferencia de tiempo de ejecución medida por el benchmark.
Ejercicio interactivo práctico
Prueba este ejercicio y completa el código de muestra.
#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)
)
*/