24

假设我在 R 中创建了一个列表并将其追加如下:

x = list(10)
x[[2]] = 20

这是否等同于

x = list(10)
x = list(10, 20)

? 我对 R 如何处理内存中的列表的具体细节不是很有经验,但我有限的理解是它往往是复制快乐的;对我来说理想的是第一个选项本质上不涉及在内存中创建另一个列表,而只是导致在内存中为附加值留出一个新位置。本质上,如果我有一个很大的列表,我不想让 R 制作另一个副本,如果我只想在它上面附加一些东西。

如果我想要的行为不是这里给出的,有没有其他方法可以获得预期的效果?

4

4 回答 4

17

我相当有信心答案是“不”。我使用以下代码仔细检查:

Rprof(tmp <- tempfile(), memory.profiling = TRUE)

x <- list()
for (i in 1:100) x[[i]] <- runif(10000)

Rprof()
summaryRprof(tmp, memory = "stats")
unlink(tmp)

输出:

# index: runif
#      vsize.small  max.vsize.small      vsize.large  max.vsize.large 
#            76411           381781           424523          1504387 
#            nodes        max.nodes     duplications tot.duplications 
#          2725878         13583136                0                0 
#          samples 
#                5 

相关部分是duplications = 0.

于 2012-10-07T20:35:12.937 回答
12

Matthew Dowle在这里的回答以及提高内存效率背后的基本原理是阻止大量在幕后通过<-[<-[[<-其他基本R操作(names等)进行复制

[[<-将复制整个x. 请参阅下面的示例

x <- list(20)
 tracemem(x)
#[1] "<0x2b0e2790>"
 x[[2]] <- 20
# tracemem[0x2b0e2790 -> 0x2adb7798]: 

你的第二个案例

x <- list(10,20)

并没有真正附加原始内容x,而是替换x为恰好是x带有附加值的原始对象的对象。

于 2012-10-07T22:25:10.653 回答
9

为了帮助我弄清楚修改列表是否会产生深拷贝或浅拷贝,我设置了一个小实验。如果修改列表会产生深层复制,那么当您修改包含大对象的列表时,与包含小对象的列表相比,它应该会更慢:

z1 <- list(runif(1e7))
z2 <- list(1:10)

system.time({
  for(i in 1:1e4) z1[1 + i] <- 1L
})
#  user  system elapsed
# 0.283   0.034   0.317
system.time({
  for(i in 1:1e4) z2[1 + i] <- 1L
})
#  user  system elapsed
# 0.284   0.034   0.319

我的计算机上的时间基本相同,这表明复制列表会产生浅拷贝,复制指向现有数据结构的指针。

于 2012-10-08T14:35:18.180 回答
5

接受了弗洛德尔的回答,但蔡斯的建议很好,所以我确认我使用他的建议使用tracemem(). 这是第一个示例,我们只是附加到列表中:

x = list(10)
tracemem(x[[1]])
# [1] "<0x2d03fa8>" #(likely different on each machine)
x[[2]] = 20
tracemem(x[[1]])
# [1] "<0x2d03fa8>"

这是第二个示例的结果,我们在其中创建了两个列表:

x = list(10)
tracemem(x[[1]])
# [1] "<0x2d03c78>"
x = list(10, 20)
tracemem(x[[1]])
# [1] "<0x2d07ff8>"

所以第一种方法似乎给出了所需的行为。

于 2012-10-07T20:51:14.230 回答