7

列名などを使用してテーブルのスライスを取得した場合、R はスライスを新しい場所に保持するためにメモリを割り当てますか? 具体的には、特に、列の深さ1と深さ2を持つテーブルがあります。2 つの最大値と最小値を含む列を追加したいと考えています。私には2つのアプローチがあります:

dd = dat[,c("depth1","depth2")]
dat$mindepth = apply(dd,1,min)
dat$maxdepth = apply(dd,1,max)
remove(dd)

また

dat$mindepth = apply(dat[,c("depth1","depth2")],1,min)
dat$maxdepth = apply(dat[,c("depth1","depth2")],1,max)

新しいメモリを使い果たしていない場合は、スライスを 1 回だけ取得します。それ以外の場合は、再割り当てを保存します。どちらの方がよいですか?大規模なデータセットを処理する場合、メモリの問題が重大になる可能性があるため、すべての邪悪なミームの根源でこれを否定しないでください.

4

1 に答える 1

6

私はこれが実際に質問の主な目的に答えていないことを知っています(@hadleyはそれを行い、信用に値します)が、あなたが提案するものには他の選択肢があります。ここではpmin()pmax()を別の解決策として使用できます。with()またはwithin()を使用して、明示的なサブセット化なしでそれを実行して、を作成できますdd

R> set.seed(1)
R> dat <- data.frame(depth1 = runif(10), depth2 = runif(10))
R> dat <- within(dat, mindepth <- pmin(depth1, depth2))
R> dat <- within(dat, maxdepth <- pmax(depth1, depth2))
R> 
R> dat
       depth1    depth2   mindepth  maxdepth
1  0.26550866 0.2059746 0.20597457 0.2655087
2  0.37212390 0.1765568 0.17655675 0.3721239
3  0.57285336 0.6870228 0.57285336 0.6870228
4  0.90820779 0.3841037 0.38410372 0.9082078
5  0.20168193 0.7698414 0.20168193 0.7698414
6  0.89838968 0.4976992 0.49769924 0.8983897
7  0.94467527 0.7176185 0.71761851 0.9446753
8  0.66079779 0.9919061 0.66079779 0.9919061
9  0.62911404 0.3800352 0.38003518 0.6291140
10 0.06178627 0.7774452 0.06178627 0.7774452

次のconfigureオプションをアクティブにしてRをコンパイルした場合にのみtracemem()、コピーがどの程度行われるかを確認できます。--enable-memory-profiling

R> set.seed(1)
R> dat <- data.frame(depth1 = runif(10), depth2 = runif(10))
R> tracemem(dat)
[1] "<0x2641cd8>"
R> dat <- within(dat, mindepth <- pmin(depth1, depth2))
tracemem[0x2641cd8 -> 0x2641a00]: within.data.frame within 
tracemem[0x2641a00 -> 0x2641878]: [<-.data.frame [<- within.data.frame within 
R> tracemem(dat)
[1] "<0x2657bc8>"
R> dat <- within(dat, maxdepth <- pmax(depth1, depth2))
tracemem[0x2657bc8 -> 0x2c765d8]: within.data.frame within 
tracemem[0x2c765d8 -> 0x2c764b8]: [<-.data.frame [<- within.data.frame within

したがって、Rはdatwithin()呼び出し中に2回コピーされたことがわかります。それをあなたの2つの提案と比較してください:

R> set.seed(1)
R> dat <- data.frame(depth1 = runif(10), depth2 = runif(10))
R> tracemem(dat)
[1] "<0x2e1ddd0>"
R> dd <- dat[,c("depth1","depth2")]
R> tracemem(dd)
[1] "<0x2df01a0>"
R> dat$mindepth = apply(dd,1,min)
tracemem[0x2df01a0 -> 0x2cf97d8]: as.matrix.data.frame as.matrix apply 
tracemem[0x2e1ddd0 -> 0x2cc0ab0]: 
tracemem[0x2cc0ab0 -> 0x2cc0b20]: $<-.data.frame $<- 
tracemem[0x2cc0b20 -> 0x2cc0bc8]: $<-.data.frame $<- 
R> tracemem(dat)
[1] "<0x26b93c8>"
R> dat$maxdepth = apply(dd,1,max)
tracemem[0x2df01a0 -> 0x2cc0e30]: as.matrix.data.frame as.matrix apply 
tracemem[0x26b93c8 -> 0x26742c8]: 
tracemem[0x26742c8 -> 0x2674358]: $<-.data.frame $<- 
tracemem[0x2674358 -> 0x2674478]: $<-.data.frame $<-

ここでは、続行する前に行列に変換されるため、へのdd呼び出しごとに1回コピーされます。出力の各ブロックの最後の3行は、新しい列を挿入するために3つのコピーが作成されていることを示しています。applyapply()ddtracememdat

2番目のオプションはどうですか?

R> set.seed(1)
R> dat <- data.frame(depth1 = runif(10), depth2 = runif(10))
R> tracemem(dat)
[1] "<0x268bc88>"
R> dat$mindepth <- apply(dat[,c("depth1","depth2")],1,min)
tracemem[0x268bc88 -> 0x26376b0]: 
tracemem[0x26376b0 -> 0x2637720]: $<-.data.frame $<- 
tracemem[0x2637720 -> 0x2637790]: $<-.data.frame $<- 
R> tracemem(dat)
[1] "<0x2466d40>"
R> dat$maxdepth <- apply(dat[,c("depth1","depth2")],1,max)
tracemem[0x2466d40 -> 0x22ae0d8]: 
tracemem[0x22ae0d8 -> 0x22ae1f8]: $<-.data.frame $<- 
tracemem[0x22ae1f8 -> 0x22ae318]: $<-.data.frame $<-

ここで、このバージョンはセットアップに関連するコピーを回避しますddが、他のすべての点で以前の提案と同様です。

もっとうまくできますか?はい。簡単な方法の1つは、最初に使用したオプションを使用することですが、両方のステートメントを実行して、次の1回の呼び出しでwithin()新しい変数mindepthと変数を作成します。maxdepthwithin()

R> set.seed(1)
R> dat <- data.frame(depth1 = runif(10), depth2 = runif(10))
R> tracemem(dat)
[1] "<0x21c4158>"
R> dat <- within(dat, { mindepth <- pmin(depth1, depth2)
+                      maxdepth <- pmax(depth1, depth2) })
tracemem[0x21c4158 -> 0x21c44a0]: within.data.frame within 
tracemem[0x21c44a0 -> 0x21c4628]: [<-.data.frame [<- within.data.frame within

このバージョンdatでは、元のバージョンの4つのコピーと比較して、の2つのコピーのみを呼び出しwithin()ます。

行列を強制datしてから挿入を行うとどうなりますか?

R> set.seed(1)
R> dat <- data.frame(depth1 = runif(10), depth2 = runif(10))
R> tracemem(dat)
[1] "<0x1f29c70>"
R> mat <- as.matrix.data.frame(dat)
tracemem[0x1f29c70 -> 0x1f09768]: as.matrix.data.frame 
R> tracemem(mat)
[1] "<0x245ff30>"
R> mat <- cbind(mat, pmin(mat[,1], mat[,2]), pmax(mat[,1], mat[,2]))
R>

datマトリックスに強制するときにの単一コピーのコストのみが発生するため、これは改善です。as.matrix.data.frame()メソッドを直接呼び出すことで少しだましました。使用したばかりの場合はas.matrix()、別のコピーが発生しますmat

これは、マトリックスがデータフレームよりもはるかに高速に使用される理由の1つを浮き彫りにします。

于 2011-04-01T12:23:45.450 に答える