附加到R中的列表会导致复制吗?

guy*_*guy 24 r list append

假设我在R中创建了一个列表并按如下方式附加到它:

x = list(10)
x[[2]] = 20
Run Code Online (Sandbox Code Playgroud)

这相当于

x = list(10)
x = list(10, 20)
Run Code Online (Sandbox Code Playgroud)

?我对R如何处理内存中的列表的具体细节不太熟悉,但我的理解有限,它往往是复制快乐; 对我来说最理想的是第一个选项并不涉及在内存中创建另一个列表,而只是在内存中为附加值留出一个新位置.基本上,如果我有一个大的列表,我不希望R再制作它的另一个副本,如果我只想附加一些东西.

如果我想要的行为不是这里给出的,那么还有其他方法可以达到预期的效果吗?

flo*_*del 16

我很自信答案是"不".我使用以下代码仔细检查:

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

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

Rprof()
summaryRprof(tmp, memory = "stats")
unlink(tmp)
Run Code Online (Sandbox Code Playgroud)

输出:

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

相关部分是duplications = 0.

  • 我不认为你的推理必然是正确的:重复在R中具有特殊含义,从技术上讲,虽然扩展向量的长度会创建一个副本,但它不是重复.请参阅R-help上的这个主题:http://r.789695.n4.nabble.com/Understanding-tracemem-td4636321.html (3认同)

mne*_*nel 12

马修Dowle的答案在这里和后面的内存效率的理由是停止幕后的众多复制的<-,[<-,[[<-和其他基本R操作(names等)

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

x <- list(20)
 tracemem(x)
#[1] "<0x2b0e2790>"
 x[[2]] <- 20
# tracemem[0x2b0e2790 -> 0x2adb7798]: 
Run Code Online (Sandbox Code Playgroud)

你的第二个案子

x <- list(10,20)
Run Code Online (Sandbox Code Playgroud)

并不是真正附加原始文件,x而是替换x为恰好是x具有附加值的原始对象.


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

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

  • `.Internal(inspect(x))`是一种更具体的方式.查看长向量的十六进制地址是否已更改. (8认同)

guy*_*guy 5

接受了 flodel 的回答,但 Chase 的提示很好,所以我使用他的建议使用tracemem(). 这是第一个示例,我们只是将其附加到列表中:

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

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

x = list(10)
tracemem(x[[1]])
# [1] "<0x2d03c78>"
x = list(10, 20)
tracemem(x[[1]])
# [1] "<0x2d07ff8>"
Run Code Online (Sandbox Code Playgroud)

因此,第一种方法似乎提供了所需的行为。