サーチ…


行操作による

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 

しかし、もっとうまくいくことができますか?ここで何が起こったのか見てみましょう:

  1. まず、 data.framematrix変換しました。 (彼はapply関数内で起こることに注意してください)。これは、非効率的で危険です。 matrixは一度に複数の列型を保持することはできません。したがって、そのような変換は、おそらく情報の損失や誤解を招く結果につながりapply(iris, 2, class)str(iris)またはsapply(iris, class)sapply(iris, class)してsapply(iris, class) )。
  2. もう1つは、各行に対して1回の操作を繰り返し実行したことです。意味、私たちはいくつかのR関数のnrow(mtcars)回を評価しなければなりnrow(mtcars) 。この特定の場合、 meanは計算上高価な関数ではないため、大きなデータセットであってもRが簡単に処理できる可能性がありますが、行ごとに標準偏差を計算する必要がある場合(高価な平方根演算を伴う) ?それは次のポイントに私たちをもたらします:
  3. 何度も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.framematrix変換しました。 rowMeansにはエラー処理メカニズムがあり、処理できないデータセットでは実行されませんが、それでも効率は低下します。

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

しかし、それでもなお、私たちはより良いことができますか?エラー処理を伴う行列変換ではなく、 mtcarsをベクトルとして使用する別の方法( data.frameは基本的にlistあり、 listvectorなので)を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


Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow