Ändere nicht die Größe von Rcpp-Vektoren
Rcpp-Vektorklassen sind als sehr dünne Hüllen um R-Vektoren konzipiert. Das hat zur Folge, dass Vergrößern oder Verkleinern bedeutet, einen neuen Vektor in der passenden Größe zu erzeugen und die relevanten Daten zu kopieren. Das ist sehr langsam und sollte vermieden werden.
Wenn möglich, solltest du deinen Code so strukturieren, dass du zuerst die endgültige Größe des Vektors berechnest und ihn dann mit genau dieser Größe allokierst.
Schauen wir uns ein Beispiel an, das die positiven Werte aus einem Vektor auswählt – äquivalent zu x[x > 0] in R. Da du vorher nicht weißt, wie viele positive Zahlen es geben wird, ist es verlockend, mit einem Vektor der Länge null zu starten und jedes gefundene Element anzuhängen. Hier ist push_back() eine Funktion, die einen Wert anhängt.
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;
}
Leider ist diese Funktion langsam, weil sie wiederholt neue Vektoren erzeugen und die Daten kopieren muss. Schau, ob du es besser machen kannst!
Diese Übung ist Teil des Kurses
R-Code mit Rcpp optimieren
Anleitung zur Übung
- Vervollständige die Definition einer effizienteren Funktion
good_select_positive_values_cpp(), um die positiven Zahlen auszuwählen.- Im ersten
for-Loop: Wenn das i-te Element vonxgrößer als null ist, erhöhen_positive_elementsum eins. - Allokiere danach einen numerischen Vektor
positive_xder Größen_positive_elements. - Im zweiten
for-Loop prüfst du erneut, ob das i-te Element vonxgrößer als null ist. - Wenn ja, setze das j-te Element von
positive_xauf das i-te Element vonxund erhöhe anschließendjum eins.
- Im ersten
bad_select_positive_values_cpp()steht dir in deinem Workspace zum Vergleich zur Verfügung. Sieh dir die Konsolenausgabe an, um den gemessenen Unterschied in der Laufzeit zu erkennen.
Interaktive Übung
Vervollständige den Beispielcode, um diese Übung erfolgreich abzuschließen.
#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)
)
*/