为什么要在 R 中复制列表?
Why is list being copied in R?
我最近一直在尝试通过阅读和试验 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 的例子相同的内存效率?
这不是您问题的直接答案,但我认为您的脚本可能会误导您。列表地址的变化data.frame并不意味着所有数据的复制。
R中的list
和data.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 个对象没有被复制。
正如目前在 pryr 的 CRAN 版本中实现的那样,当 运行 在当前 R 中时,address() 函数会将其参数的引用数量增加到 2。一旦发生这种情况,就必须复制对象更换电话。 refs() 函数实现避免添加引用,所以如果你只是打印 refs(y) 你会看到它保持在 1 并且不会有重复。
我最近一直在尝试通过阅读和试验 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 的例子相同的内存效率?
这不是您问题的直接答案,但我认为您的脚本可能会误导您。列表地址的变化data.frame并不意味着所有数据的复制。
R中的list
和data.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 个对象没有被复制。
正如目前在 pryr 的 CRAN 版本中实现的那样,当 运行 在当前 R 中时,address() 函数会将其参数的引用数量增加到 2。一旦发生这种情况,就必须复制对象更换电话。 refs() 函数实现避免添加引用,所以如果你只是打印 refs(y) 你会看到它保持在 1 并且不会有重复。