R Language
Rコードベクトル化のベストプラクティス
サーチ…
行操作による
Rコードをベクトル化する際の鍵は、「行操作による」またはR関数のメソッドディスパッチを削減または排除することです。
つまり、一見して各行の平均を計算するなどの「行操作によって」必要となる問題に近づくと、自分自身に尋ねる必要があります。
- 私が扱っているデータセットのクラスは何ですか?
- R関数の反復的な評価を必要とせずにこれを達成できる既存のコンパイル済みコードはありますか?
- そうでない場合は、行ではなく列でこれらの操作を実行できますか?
- 最後に、単純な
apply
ループを実行するのではなく、複雑なベクター化されたコードを開発することに多くの時間を費やす価値apply
ますか?言い換えれば、Rが単純なループを使用して効率的に処理できないほどデータを大きく/洗練されたものにしていますか?
メモリの事前割り振り問題と増加するオブジェクトをループに入れて、ループ内でのR関数のディスパッチや再評価を回避apply
方法について、この例で取り上げます。
行ごとに平均を計算する標準/簡単な方法は次のとおりです。
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
しかし、もっとうまくいくことができますか?ここで何が起こったのか見てみましょう:
- まず、
data.frame
をmatrix
変換しました。 (彼はapply
関数内で起こることに注意してください)。これは、非効率的で危険です。matrix
は一度に複数の列型を保持することはできません。したがって、そのような変換は、おそらく情報の損失や誤解を招く結果につながりapply(iris, 2, class)
とstr(iris)
またはsapply(iris, class)
とsapply(iris, class)
してsapply(iris, class)
)。 - もう1つは、各行に対して1回の操作を繰り返し実行したことです。意味、私たちはいくつかのR関数の
nrow(mtcars)
回を評価しなければなりnrow(mtcars)
。この特定の場合、mean
は計算上高価な関数ではないため、大きなデータセットであってもRが簡単に処理できる可能性がありますが、行ごとに標準偏差を計算する必要がある場合(高価な平方根演算を伴う) ?それは次のポイントに私たちをもたらします: - 何度もR関数を評価しましたが、すでにこの操作のコンパイル版がありますか?
実際には、
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
これは、行操作によるものではないため、R関数の反復的な評価は必要ありません。 しかし 、私たちはまだdata.frame
をmatrix
変換しました。 rowMeans
にはエラー処理メカニズムがあり、処理できないデータセットでは実行されませんが、それでも効率は低下します。
rowMeans(iris)
Error in rowMeans(iris) : 'x' must be numeric
しかし、それでもなお、私たちはより良いことができますか?エラー処理を伴う行列変換ではなく、 mtcars
をベクトルとして使用する別の方法( data.frame
は基本的にlist
あり、 list
はvector
なので)をdata.frame
ことがdata.frame
ます。
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
速度の向上が可能になると、列名やエラー処理( NA
処理を含む)が失われました。
もう一つの例は、基数Rを使ってグループごとに平均を計算することです
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
それでも、私たちは基本的にループ内のR関数を評価していますが、ループは内部のC関数に隠されています(CループかRループかはほとんど関係ありません)。
私たちはそれを避けることができますか?まあ、Rにはrowsum
というコンパイルされた関数があるので、次のようにすることができます:
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
最初に行列に変換しなければならなかったのですが。
この時点で、現在のデータ構造が最も適切かどうかという疑問があるかもしれません。 data.frame
はベストプラクティスですか?または効率を上げるために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
1つは単に行うことができます:
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
他方では、分散の公式に従って、この操作を完全にベクトル化することもできる
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