IniziaInizia gratis

Non cambiare la dimensione dei vettori Rcpp

Le classi vettoriali di Rcpp sono progettate come sottili wrapper attorno ai vettori di R. Questo implica che aumentarne o ridurne la dimensione significa creare ogni volta un nuovo vettore della dimensione corretta e copiarvi i dati rilevanti. È un'operazione molto lenta, quindi andrebbe evitata.

Se possibile, struttura il codice in modo da calcolare prima la dimensione finale del vettore e poi allocarlo con quella dimensione.

Vediamo un esempio che seleziona i valori positivi da un vettore, equivalente a x[x > 0] in R. Poiché non sai in anticipo quanti numeri positivi ci saranno, è allettante partire da un vettore di lunghezza zero e aggiungere un valore ogni volta che ne trovi uno. Qui, push_back() è una funzione che accoda un valore.

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;
}

Purtroppo, questa funzione sarà lenta perché deve creare ripetutamente nuovi vettori e copiare i dati. Vediamo se riesci a fare di meglio!

Questo esercizio fa parte del corso

Ottimizzare il codice R con Rcpp

Visualizza il corso

Istruzioni dell'esercizio

  • Completa la definizione di una funzione più efficiente, good_select_positive_values_cpp() per selezionare i numeri positivi.
    • Nel primo ciclo for, se l'elemento i-esimo di x è maggiore di zero, aggiungi uno a n_positive_elements.
    • Dopo quel ciclo for, alloca un vettore numerico, positive_x, di dimensione n_positive_elements.
    • Nel secondo ciclo for, controlla di nuovo se l'elemento i-esimo di x è maggiore di zero.
    • Quando lo è, imposta l'elemento j-esimo di positive_x all'elemento i-esimo di x, poi aggiungi uno a j.
  • bad_select_positive_values_cpp() è disponibile nel tuo workspace per confronto. Esamina l'output della console per vedere la differenza di tempo di esecuzione nel benchmark.

Esercizio pratico interattivo

Prova a risolvere questo esercizio completando il codice di esempio.

#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)
)
*/
Modifica ed esegui il codice