Inizia subitoInizia 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 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 interattivo pratico

Prova questo esercizio completando questo 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