Suche…


Durch Zeilenoperationen

Der Schlüssel bei der Vektorisierung von R-Code besteht darin, "Zeilenoperationen" oder das Methoden-Dispatching von R-Funktionen zu reduzieren oder zu eliminieren.

Das heißt, wenn man sich einem Problem nähert, das auf den ersten Blick "durch Zeilenoperationen" erforderlich ist, wie zum Beispiel die Berechnung der Mittelwerte jeder Zeile, muss man sich fragen:

  • Was sind die Klassen der Datensätze, mit denen ich zu tun habe?
  • Gibt es einen vorhandenen kompilierten Code, der dies ohne wiederholte Bewertung von R-Funktionen erreichen kann?
  • Wenn nicht, kann ich diese Operationen nach Spalten statt nach Zeilen ausführen?
  • Lohnt es sich schließlich, viel Zeit darauf zu verwenden, komplizierten vektorisierten Code zu entwickeln, anstatt nur eine einfache apply auszuführen? Mit anderen Worten: Sind die Daten groß genug, dass R sie nicht mit einer einfachen Schleife effizient verarbeiten kann?

Abgesehen von der Speicherzuordnung und dem wachsenden Objekt in Schleifen werden wir uns in diesem Beispiel darauf konzentrieren, wie Sie das apply Schleifen, das Methoden-Dispatching oder die Neubewertung von R-Funktionen in Schleifen möglicherweise vermeiden.

Eine einfache / einfache Methode zum Berechnen des Mittelwerts pro Zeile wäre:

apply(mtcars, 1, mean)
          Mazda RX4       Mazda RX4 Wag          Datsun 710      Hornet 4 Drive   Hornet Sportabout             Valiant          Duster 360 
           29.90727            29.98136            23.59818            38.73955            53.66455            35.04909            59.72000 
          Merc 240D            Merc 230            Merc 280           Merc 280C          Merc 450SE          Merc 450SL         Merc 450SLC 
           24.63455            27.23364            31.86000            31.78727            46.43091            46.50000            46.35000 
 Cadillac Fleetwood Lincoln Continental   Chrysler Imperial            Fiat 128         Honda Civic      Toyota Corolla       Toyota Corona 
           66.23273            66.05855            65.97227            19.44091            17.74227            18.81409            24.88864 
   Dodge Challenger         AMC Javelin          Camaro Z28    Pontiac Firebird           Fiat X1-9       Porsche 914-2        Lotus Europa 
           47.24091            46.00773            58.75273            57.37955            18.92864            24.77909            24.88027 
     Ford Pantera L        Ferrari Dino       Maserati Bora          Volvo 142E 
           60.97182            34.50818            63.15545            26.26273 

Aber können wir es besser machen? Mal sehen was hier passiert ist:

  1. Zuerst haben wir ein data.frame in eine matrix konvertiert. (Beachten Sie, dass dies innerhalb der apply Funktion geschieht.) Dies ist sowohl ineffizient als auch gefährlich. Eine matrix kann nicht mehrere Spaltentypen gleichzeitig enthalten. Daher wird eine solche Konvertierung wahrscheinlich zu Informationsverlust und manchmal zu irreführenden Ergebnissen führen (vergleiche apply(iris, 2, class) mit str(iris) oder mit sapply(iris, class) ).
  2. Zweitens haben wir eine Operation wiederholt, einmal für jede Zeile. Das heißt, wir mussten einige R-Funktionen nrow(mtcars) mal auswerten. In diesem speziellen Fall ist der mean keine rechenintensive Funktion, daher könnte R wahrscheinlich auch für große Datenmengen leicht damit umgehen. Was passiert, wenn wir die Standardabweichung pro Zeile berechnen müssen (was eine teure Quadratwurzeloperation erfordert)? ? Was uns zum nächsten Punkt bringt:
  3. Wir haben die R-Funktion viele Male ausgewertet, aber vielleicht gibt es bereits eine kompilierte Version dieser Operation?

In der Tat könnten wir einfach tun:

rowMeans(mtcars)
          Mazda RX4       Mazda RX4 Wag          Datsun 710      Hornet 4 Drive   Hornet Sportabout             Valiant          Duster 360 
           29.90727            29.98136            23.59818            38.73955            53.66455            35.04909            59.72000 
          Merc 240D            Merc 230            Merc 280           Merc 280C          Merc 450SE          Merc 450SL         Merc 450SLC 
           24.63455            27.23364            31.86000            31.78727            46.43091            46.50000            46.35000 
 Cadillac Fleetwood Lincoln Continental   Chrysler Imperial            Fiat 128         Honda Civic      Toyota Corolla       Toyota Corona 
           66.23273            66.05855            65.97227            19.44091            17.74227            18.81409            24.88864 
   Dodge Challenger         AMC Javelin          Camaro Z28    Pontiac Firebird           Fiat X1-9       Porsche 914-2        Lotus Europa 
           47.24091            46.00773            58.75273            57.37955            18.92864            24.77909            24.88027 
     Ford Pantera L        Ferrari Dino       Maserati Bora          Volvo 142E 
           60.97182            34.50818            63.15545            26.26273 

Dies beinhaltet keine zeilenweisen Operationen und daher keine wiederholte Bewertung von R-Funktionen. Ein data.frame in eine matrix konvertiert. Obwohl rowMeans über einen Fehlerbehandlungsmechanismus verfügt, der nicht mit einem Datensatz ausgeführt werden kann, für den er nicht rowMeans ist, hat dies dennoch Kosten für die Effizienz.

rowMeans(iris)
Error in rowMeans(iris) : 'x' must be numeric

Aber können wir es trotzdem besser machen? Wir könnten anstelle einer mtcars mit Fehlerbehandlung versuchen, eine andere Methode zu verwenden, die es uns ermöglicht, mtcars als Vektor zu verwenden (da data.frame im Wesentlichen eine list und eine list ein vector ).

Reduce(`+`, mtcars)/ncol(mtcars)
 [1] 29.90727 29.98136 23.59818 38.73955 53.66455 35.04909 59.72000 24.63455 27.23364 31.86000 31.78727 46.43091 46.50000 46.35000 66.23273 66.05855
[17] 65.97227 19.44091 17.74227 18.81409 24.88864 47.24091 46.00773 58.75273 57.37955 18.92864 24.77909 24.88027 60.97182 34.50818 63.15545 26.26273

Nun haben wir wegen möglicher Geschwindigkeitssteigerung Spaltennamen und Fehlerbehandlung (einschließlich NA Behandlung) verloren.


Ein anderes Beispiel wäre die Berechnung des Mittelwerts nach Gruppe, wobei wir die Basis R verwenden könnten

aggregate(. ~ cyl, mtcars, mean)
cyl      mpg     disp        hp     drat       wt     qsec        vs        am     gear     carb
1   4 26.66364 105.1364  82.63636 4.070909 2.285727 19.13727 0.9090909 0.7272727 4.090909 1.545455
2   6 19.74286 183.3143 122.28571 3.585714 3.117143 17.97714 0.5714286 0.4285714 3.857143 3.428571
3   8 15.10000 353.1000 209.21429 3.229286 3.999214 16.77214 0.0000000 0.1428571 3.285714 3.500000

Grundsätzlich evaluieren wir eine R-Funktion in einer Schleife, aber die Schleife ist jetzt in einer internen C-Funktion verborgen (es spielt keine Rolle, ob es sich um eine C- oder eine R-Schleife handelt).

Könnten wir es vermeiden? Nun, es gibt eine kompilierte Funktion in R, die rowsum heißt, daher könnten wir folgendes tun:

rowsum(mtcars[-2], mtcars$cyl)/table(mtcars$cyl)
mpg     disp        hp     drat       wt     qsec        vs        am     gear     carb
4 26.66364 105.1364  82.63636 4.070909 2.285727 19.13727 0.9090909 0.7272727 4.090909 1.545455
6 19.74286 183.3143 122.28571 3.585714 3.117143 17.97714 0.5714286 0.4285714 3.857143 3.428571
8 15.10000 353.1000 209.21429 3.229286 3.999214 16.77214 0.0000000 0.1428571 3.285714 3.500000

Allerdings mussten wir auch zuerst in eine Matrix konvertieren.

Zu diesem Punkt stellen wir uns vielleicht die Frage, ob unsere derzeitige Datenstruktur die geeignetste ist. Ist ein data.frame die beste data.frame ? Oder sollte man einfach auf eine matrix umsteigen, um die Effizienz zu steigern?


Zeilenoperationen werden immer teurer (sogar in Matrizen), da wir jedes Mal teure Funktionen auswerten. Betrachten wir eine Abweichungsberechnung anhand eines Zeilenbeispiels.

Nehmen wir an, wir haben eine Matrix m :

set.seed(100)
m <- matrix(sample(1e2), 10)
m
      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
 [1,]    8   33   39   86   71  100   81   68   89    84
 [2,]   12   16   57   80   32   82   69   11   41    92
 [3,]   62   91   53   13   42   31   60   70   98    79
 [4,]   66   94   29   67   45   59   20   96   64     1
 [5,]   36   63   76    6   10   48   85   75   99     2
 [6,]   18    4   27   19   44   56   37   95   26    40
 [7,]    3   24   21   25   52   51   83   28   49    17
 [8,]   46    5   22   43   47   74   35   97   77    65
 [9,]   55   54   78   34   50   90   30   61   14    58
[10,]   88   73   38   15    9   72    7   93   23    87

Man könnte einfach tun:

apply(m, 1, var)
[1]  871.6556  957.5111  699.2111  941.4333 1237.3333  641.8222  539.7889  759.4333  500.4889 1255.6111

Auf der anderen Seite könnte man diese Operation auch vollständig vektorisieren, indem man der Varianzformel folgt

RowVar <- function(x) {
  rowSums((x - rowMeans(x))^2)/(dim(x)[2] - 1)
}
RowVar(m)
[1]  871.6556  957.5111  699.2111  941.4333 1237.3333  641.8222  539.7889  759.4333  500.4889 1255.6111


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow