R Language
B-praxis för R-kodvektorisering
Sök…
Genom radoperationer
Nyckeln i att vektorisera R-kod är att reducera eller eliminera "genom radoperationer" eller metodutsändning av R-funktioner.
Det betyder att man när man närmar sig ett problem som vid första anblicken kräver ”genom radoperationer”, som att beräkna medel för varje rad, måste man fråga sig själva:
- Vilka är de klasser i datasätten jag har att göra med?
- Finns det en befintlig kompilerad kod som kan uppnå detta utan behov av upprepande utvärdering av R-funktioner?
- Om inte, kan jag göra dessa åtgärder i kolumner istället för rad?
- Slutligen, är det värt att spendera mycket tid på att utveckla komplicerad vektoriserad kod istället för att bara köra en enkel
apply
? Med andra ord, är datana tillräckligt stora / sofistikerade att R inte kan hantera dem effektivt med en enkel slinga?
Om man lägger åt minnesfördelningsfrågan och växer objekt i slingor kommer vi i detta exempel att fokusera på hur man eventuellt kan undvika att apply
slingor, metodutsändning eller omvärdera R-funktioner inom slingor.
Ett standard / enkelt sätt att beräkna medelvärde för rad skulle vara:
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
Men kan vi göra bättre? Låt oss se vad som hände här:
- Först konverterade vi en
data.frame
till enmatrix
. (Observera att hans händer inomapply
.) Detta är både ineffektivt och farligt. enmatrix
kan inte innehålla flera kolumntyper åt gången. Därför kommer en sådan omvandling förmodligen att leda till förlust av information och ibland till vilseledande resultat (jämförapply(iris, 2, class)
medstr(iris)
eller medsapply(iris, class)
). - För det andra utförde vi en operation repetitivt, en gång för varje rad.
nrow(mtcars)
vi var tvungna att utvärdera några R-funktionernrow(mtcars)
gånger. I detta specifika fall ärmean
inte en beräkningsdyr funktion, därför kan R sannolikt enkelt hantera den även för en stor datauppsättning, men vad skulle hända om vi behöver beräkna standardavvikelsen för rad (vilket innebär en dyr kvadratrotoperation) ? Som leder oss till nästa punkt: - Vi utvärderade R-funktionen många gånger, men kanske finns det redan en sammanställd version av denna operation?
Vi kan faktiskt bara göra:
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
Detta involverar ingen i rad operationer och därför ingen upprepande utvärdering av R-funktioner. Men vi konverterade fortfarande en data.frame
till en matrix
. Även om rowMeans
har en felhanteringsmekanism och den kommer inte att köras på en datauppsättning som den inte kan hantera, har den fortfarande en effektivitetskostnad.
rowMeans(iris)
Error in rowMeans(iris) : 'x' must be numeric
Men ändå, kan vi göra bättre? Vi kan försöka istället för en matriskonvertering med felhantering, en annan metod som gör att vi kan använda mtcars
som en vektor (eftersom en data.frame
huvudsak är en list
och en list
är en 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
För möjlig hastighetsökning förlorade vi kolumnnamn och felhantering (inklusive NA
hantering).
Ett annat exempel skulle vara att beräkna medelvärde per grupp, använda bas R som vi kan prova
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
Fortfarande utvärderar vi i princip en R-funktion i en slinga, men slingan är nu dold i en intern C-funktion (det spelar ingen roll om det är en C eller en R-slinga).
Kan vi undvika det? Det finns väl en kompilerad funktion i R som kallas rowsum
, därför kan vi göra:
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
Även om vi var tvungna att konvertera till en matris först också.
Här kan vi ifrågasätta om vår nuvarande datastruktur är den mest lämpliga. Är en data.frame
den bästa praxis? Eller bör man bara byta till en matrix
för att få effektivitet?
Efter rad kommer operationer att bli mer och dyrare (även i matriser) när vi börjar utvärdera dyra funktioner varje gång. Låter oss överväga en variansberäkning med radexempel.
Låt oss säga att vi har en matris 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 kan helt enkelt göra:
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
Å andra sidan kan man också fullständigt vektorisera denna operation genom att följa formeln för varians
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