表切片占用R中的内存吗?

hig*_*dth 8 optimization memory-management r premature-optimization

如果我使用一个表,比如列名,是否R分配内存以将切片保存在新位置?具体来说,我有一个包含列depth1和depth2的表格.我想添加包含两者的最大值和最小值的列.我有两种方法:

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

要么

dat$mindepth = apply(dat[,c("depth1","depth2")],1,min)
dat$maxdepth = apply(dat[,c("depth1","depth2")],1,max)
Run Code Online (Sandbox Code Playgroud)

如果我没有使用新的内存,我宁愿只切片一次,否则我想保存重新分配.哪一个更好?在处理大型数据集时,内存问题可能至关重要,因此请不要将其与所有邪恶模因的根源进行暗示.

Rei*_*son 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
Run Code Online (Sandbox Code Playgroud)

我们可以查看复制的次数,tracemem()前提是您的R是在激活了以下配置选项的情况下编译的--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
Run Code Online (Sandbox Code Playgroud)

所以我们看到R dat在每次within()调用期间复制了两次.与你的两个建议相比:

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 $<-
Run Code Online (Sandbox Code Playgroud)

这里,dd在每次调用中复制一次,apply因为在继续之前apply()转换dd为矩阵.每个tracemem输出块中的最后三行表示dat正在插入新列的三个副本.

你的第二个选择呢?

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 $<-
Run Code Online (Sandbox Code Playgroud)

此版本避免了设置中涉及的副本dd,但在所有其他方面与您之前的建议类似.

我们可以做得更好吗?是的,一种简单的方法是使用within()我开始使用的选项,但执行两个语句以在一次调用中创建新的mindepthmaxdepth变量within():

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
Run Code Online (Sandbox Code Playgroud)

在此版本中,我们仅调用两个副本,dat与原始within()版本的4个副本进行比较.

如果我们强制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>
Run Code Online (Sandbox Code Playgroud)

这是一种改进,因为我们只会dat在强制转换为矩阵时产生单一副本的成本.我通过as.matrix.data.frame()直接调用该方法作弊了一下.如果我们刚刚使用过,as.matrix()我们会再发一份mat.

这突出了为什么矩阵使用速度比数据帧快得多的一个原因.