ComenzarEmpieza gratis

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

Ver curso

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 de x es mayor que cero, suma uno a n_positive_elements.
    • Después de ese bucle for, reserva un vector numérico, positive_x, de tamaño n_positive_elements.
    • En el segundo bucle for, vuelve a comprobar si el elemento i-ésimo de x es mayor que cero.
    • Cuando lo sea, asigna el elemento j-ésimo de positive_x al elemento i-ésimo de x y luego suma uno a j.
  • 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)
)
*/
Editar y ejecutar código