16

約 1,000 万行の非常に大きなデータ フレームがあります。列xyがあり、私が望むのは計算することです

hypot <- function(x) {sqrt(x[1]^2 + x[2]^2)}

行ごとに。これを使用applyすると、多くの時間 (小さいサイズからの補間で約 5 分) とメモリが必要になります。

しかし、私には多すぎるように思われるので、さまざまなことを試しました。

  • 関数をコンパイルhypotすると、時間が約 10% 短縮されます
  • の関数を使用するとplyr、実行時間が大幅に増加します。

これを行うための最速の方法は何ですか?

4

3 に答える 3

23

どうwith(my_data,sqrt(x^2+y^2))ですか?

set.seed(101)
d <- data.frame(x=runif(1e5),y=runif(1e5))

library(rbenchmark)

2 つの異なる行ごとの関数で、1 つはベクトル化を利用しています。

hypot <- function(x) sqrt(x[1]^2+x[2]^2)
hypot2 <- function(x) sqrt(sum(x^2))

これらもコンパイルしてみてください:

library(compiler)
chypot <- cmpfun(hypot)
chypot2 <- cmpfun(hypot2)

benchmark(sqrt(d[,1]^2+d[,2]^2),
          with(d,sqrt(x^2+y^2)),
          apply(d,1,hypot),
          apply(d,1,hypot2),
          apply(d,1,chypot),
          apply(d,1,chypot2),
          replications=50)

結果:

                       test replications elapsed relative user.self sys.self
5       apply(d, 1, chypot)           50  61.147  244.588    60.480    0.172
6      apply(d, 1, chypot2)           50  33.971  135.884    33.658    0.172
3        apply(d, 1, hypot)           50  63.920  255.680    63.308    0.364
4       apply(d, 1, hypot2)           50  36.657  146.628    36.218    0.260
1 sqrt(d[, 1]^2 + d[, 2]^2)           50   0.265    1.060     0.124    0.144
2  with(d, sqrt(x^2 + y^2))           50   0.250    1.000     0.100    0.144

予想どおり、with()ソリューションと Tyler Rinker 風の列インデックス ソリューションは本質的に同一です。hypot2元の 2 倍の速さですhypot(ただし、ベクトル化されたソリューションよりも約 150 倍遅い)。OPですでに指摘されているように、コンパイルはあまり役に立ちません。

于 2012-12-20T19:21:26.360 に答える
12

Ben Bolkersの回答は包括的ですが、applydata.framesで避けるべき他の理由を説明します。

applydata.frameをマトリックスに変換します。これにより、コピー(時間とメモリの浪費)が作成されるだけでなく、意図しない型変換が発生する可能性があります。

1000万行のデータがあることを考えるとdata.table、メモリと時間の観点から効率的に処理できるパッケージを検討することをお勧めします。


たとえば、tracemem

x <- apply(d,1, hypot2)
tracemem[0x2f2f4410 -> 0x2f31b8b8]: as.matrix.data.frame as.matrix apply 

次に、の列に割り当てると、これはさらに悪化します。d

d$x <- apply(d,1, hypot2)
tracemem[0x2f2f4410 -> 0x2ee71cb8]: as.matrix.data.frame as.matrix apply 
tracemem[0x2f2f4410 -> 0x2fa9c878]: 
tracemem[0x2fa9c878 -> 0x2fa9c3d8]: $<-.data.frame $<- 
tracemem[0x2fa9c3d8 -> 0x2fa9c1b8]: $<-.data.frame $<- 

4部!--1000万行で、それはおそらくいつか来てあなたを噛むでしょう。

を使用する場合、ベクトルに割り当てる場合は関係withありませんcopying

y <- with(d, sqrt(x^2 + y^2))

ただし、data.frameの列に割り当てる場合はありますd

d$y <- with(d, sqrt(x^2 + y^2))
tracemem[0x2fa9c1b8 -> 0x2faa00d8]: 
tracemem[0x2faa00d8 -> 0x2faa0f48]: $<-.data.frame $<- 
tracemem[0x2faa0f48 -> 0x2faa0d08]: $<-.data.frame $<- 

さて、を使用data.table:=て参照により割り当てる場合(コピーなし)

 library(data.table)
 DT <- data.table(d)



tracemem(DT)
[1] "<0x2d67a9a0>"
DT[,y := sqrt(x^2 + y^2)]

コピーはありません!


おそらくここで修正されますが、考慮すべきもう1つのメモリの問題は、sqrt(x^2+y^2))4つの一時変数を(内部的に)作成 しx^2、次にy^2x^2 + y^2sqrt(x^2 + y^2))

以下は遅くなりますが、作成される変数は2つだけです。

 DT[, rowid := .I] # previous option: DT[, rowid := seq_len(nrow(DT))]
 DT[, y2 := sqrt(x^2 + y^2), by = rowid]
于 2012-12-20T23:04:05.167 に答える
3

Rはベクトル化されているので、次を使用できます。もちろん、独自のマトリックスをプラグインします。

X = t(matrix(1:4, 2, 2))^2
>      [,1] [,2]
 [1,]    1    4
 [2,]    9   16

rowSums(X)^0.5

素晴らしくて効率的です:)

于 2012-12-21T01:53:31.097 に答える