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
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 dixè maggiore di zero, aggiungi uno an_positive_elements. - Dopo quel ciclo for, alloca un vettore numerico,
positive_x, di dimensionen_positive_elements. - Nel secondo ciclo
for, controlla di nuovo se l'elemento i-esimo dixè maggiore di zero. - Quando lo è, imposta l'elemento j-esimo di
positive_xall'elemento i-esimo dix, poi aggiungi uno aj.
- Nel primo ciclo
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)
)
*/