R Language
Bewährte Methoden für die Vektorisierung von R-Codes
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:
- Zuerst haben wir ein
data.frame
in einematrix
konvertiert. (Beachten Sie, dass dies innerhalb derapply
Funktion geschieht.) Dies ist sowohl ineffizient als auch gefährlich. Einematrix
kann nicht mehrere Spaltentypen gleichzeitig enthalten. Daher wird eine solche Konvertierung wahrscheinlich zu Informationsverlust und manchmal zu irreführenden Ergebnissen führen (vergleicheapply(iris, 2, class)
mitstr(iris)
oder mitsapply(iris, class)
). - 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 dermean
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: - 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