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 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)
)
*/