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:

  1. Först konverterade vi en data.frame till en matrix . (Observera att hans händer inom apply .) Detta är både ineffektivt och farligt. en matrix 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ör apply(iris, 2, class) med str(iris) eller med sapply(iris, class) ).
  2. 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-funktioner nrow(mtcars) gånger. I detta specifika fall är mean 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:
  3. 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


Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow