LoslegenKostenlos loslegen

Ä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

Kurs anzeigen

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 von x größer als null ist, erhöhe n_positive_elements um eins.
    • Allokiere danach einen numerischen Vektor positive_x der Größe n_positive_elements.
    • Im zweiten for-Loop prüfst du erneut, ob das i-te Element von x größer als null ist.
    • Wenn ja, setze das j-te Element von positive_x auf das i-te Element von x und erhöhe anschließend j um eins.
  • 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)
)
*/
Code bearbeiten und ausführen