【问题标题】:Why is list being copied in R?为什么列表在R中被复制?
【发布时间】:2015-05-17 10:25:26
【问题描述】:

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

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 在内存管理方面正在变得越来越好,而上述结果表明它正在变得更糟。尽管我可能会做一些愚蠢的事情或误解一些重要的事情。谁能告诉我为什么我的复制没有像哈德利的例子一样的记忆效率?

【问题讨论】:

  • 你在使用 RStudio 吗?我找到了这条注释:“请注意,如果您使用的是 RStudio,refs() 将始终返回 2:环境浏览器会引用您在命令行上创建的每个对象。”在adv-r.had.co.nz/memory.html#modification
  • 我也看到了那个注释,但是:(1)我认为 Hadley 在这个例子中也在使用 RStudio,因为他是 RStudio 的首席科学家,(2)虽然 refs 可能被人为夸大了,但我不相信地址应该改变,(3)我在R中重新创建了这个例子并遇到了同样的问题。
  • 击败我。在 Linux 下尝试看看这是否与操作系统有关,但得到的结果与 Win7 相同。
  • 谁能解释为什么十六进制内存地址在遍历数据帧时增加,但在遍历列表时减少?
  • 你得到哪个结果,我的还是 Hadley 的?

标签: r list for-loop memory dataframe


【解决方案1】:

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

listdata.frame 在 R 中是 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 个地址发生了变化。赋值&lt;-只复制对应R对象的数据,即x[[i]]y[[i]]。剩下的 4 个对象没有被复制。

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 如果你只使用tracemem,你也可以看到没有重复。
    • 太棒了!感谢卢克和约书亚的帮助!
    猜你喜欢
    • 1970-01-01
    • 2018-03-29
    • 2015-04-08
    • 2021-05-24
    • 1970-01-01
    • 1970-01-01
    • 2016-06-25
    • 1970-01-01
    • 2018-08-17
    相关资源
    最近更新 更多