为什么列表被复制到R?

时间:2015-03-15 05:44:49

标签: r list for-loop memory dataframe

我最近一直试图通过阅读和试验R中的内存使用来提高我的R编程技能。我最近试图在Hadley的“Advanced R”中重新创建一个例子,但得到了不同的结果。在this page的底部,Hadley设置了以下示例:

x <- data.frame(matrix(runif(100 * 1e4), ncol = 100))
medians <- vapply(x, median, numeric(1))

然后通过显示

来演示原始和非原始之间的区别
for(i in 1:5) {
  x[, i] <- x[, i] - medians[i]
  print(c(address(x), refs(x)))
}
每次循环运行时都会复制

(因为x是数据帧)。而

y <- as.list(x)

for(i in 1:5) {
  y[[i]] <- y[[i]] - medians[i]
  print(c(address(y), refs(y)))
}

修改y到位(因为y已被转换为列表,这是一个基本对象)。但是,当我重新创建此代码时,我发现在两个实例中都会复制:

> x <- data.frame(matrix(runif(100 * 1e4), ncol = 100))
> medians <- vapply(x, median, numeric(1))
> 
> for(i in 1:5) {
+   x[, i] <- x[, i] - medians[i]
+   print(c(address(x), refs(x)))
+ }
[1] "0x10e4e6770" "2"          
[1] "0x10e46c420" "2"          
[1] "0x121110180" "2"          
[1] "0x11c2c26d0" "2"          
[1] "0x121151db0" "2"  


> x <- data.frame(matrix(runif(100 * 1e4), ncol = 100))
> medians <- vapply(x, median, numeric(1))
> y <- as.list(x)
> 
> for(i in 1:5) {
+   y[[i]] <- y[[i]] - medians[i]
+   print(c(address(y), refs(y)))
+ }
[1] "0x132aea2b0" "2"          
[1] "0x1211839e0" "2"          
[1] "0x11c237ea0" "2"          
[1] "0x121169a80" "2"          
[1] "0x10993f460" "2" 

看来Hadley在他的例子中至少使用了R 3.1.0,而我正在使用R 3.1.2(在Mac上)。然而,我读过的所有内容都表明随着时间的推移,R的记忆管理越来越好,而上述结果表明它的情况越来越糟。虽然我可能会做一些愚蠢或误解的事情。任何人都可以向我灌输为什么我的复制与Hadley的例子没有相同的记忆效率吗?

2 个答案:

答案 0 :(得分:2)

正如当前在pryr的CRAN版本中实现的那样,当在当前R中运行时,address()函数会将其参数的引用数量增加到2.一旦发生这种情况,就必须复制该对象以进行替换调用。 refs()函数实现避免添加引用,因此如果您只是打印refs(y),您将看到它保持为1并且不会重复。

答案 1 :(得分:0)

这不是您问题的直接答案,但我认为您的脚本可能会误导您。更改列表和data.frame的地址并不意味着所有数据的副本。

R中的

listdata.frame是R对象的向量。在您的脚本中,R应该只复制替换的R对象,因此更改列表的地址。

例如:

x <- data.frame(matrix(runif(100 * 1e4), ncol = 100))
medians <- vapply(x, median, numeric(1))

for(i in 1:5) {
  print(sprintf("===%d===", i))
  for(j in 1:5) print(sprintf("%s(%d)", address(x[[j]]), j))
  x[, i] <- x[, i] - medians[i]
  print(c(address(x), refs(x)))
  for(j in 1:5) print(sprintf("%s(%d)", address(x[[j]]), j))
}

y <- as.list(x)

for(i in 1:5) {
  print(sprintf("===%d===", i))
  for(j in 1:5) print(sprintf("%s(%d)", address(y[[j]]), j))
  y[[i]] <- y[[i]] - medians[i]
  for(j in 1:5) print(sprintf("%s(%d)", address(y[[j]]), j))
}

您应该看到每次迭代中只更改了1个地址。赋值<-仅复制相应R对象的数据,即x[[i]]y[[i]]。不会复制4个左对象。