在修改属性时有没有防止“复制-修改”(copy-on-modify)的方法?

4

以下代码中制作了矩阵的副本,这让我感到惊讶:

> (m <- matrix(1:12, nrow = 3))
     [,1] [,2] [,3] [,4]
[1,]    1    4    7   10
[2,]    2    5    8   11
[3,]    3    6    9   12
> tracemem(m)
[1] "<000001E2FC1E03D0>"
> str(m)
 int [1:3, 1:4] 1 2 3 4 5 6 7 8 9 10 ...
> attr(m, "dim") <- 4:3
tracemem[0x000001e2fc1e03d0 -> 0x000001e2fcb05008]: 
> m
     [,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12
> str(m)
 int [1:4, 1:3] 1 2 3 4 5 6 7 8 9 10 ...

这个有用吗?可以避免吗?


编辑:我跟GKi的结果不一样。

> sessionInfo()
R version 4.0.3 (2020-10-10)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 19041)

Matrix products: default

locale:
[1] LC_COLLATE=French_France.1252  LC_CTYPE=French_France.1252    LC_MONETARY=French_France.1252
[4] LC_NUMERIC=C                   LC_TIME=French_France.1252    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

loaded via a namespace (and not attached):
[1] compiler_4.0.3 tools_4.0.3   
> m <- matrix(1:12, nrow = 3)
> tracemem(m)
[1] "<000001F8DB2C7D90>"
> attr(m, "dim") <- c(4, 3)
tracemem[0x000001f8db2c7d90 -> 0x000001f8db2d93f0]: 

一个不同之处在于我不使用BLAS库……

2个回答

4
我使用的是R 3.6.3版本,确实会复制。如果想要修改属性而不进行复制,可以使用data.table包中的setattr函数:
library(data.table)

m <- matrix(1:12, nrow = 3)
.Internal(inspect(m))

setattr(m, "dim", c(4L,3L))
.Internal(inspect(m))

所以这是可以避免的。但它有用吗?你能说出为什么GKi似乎没有相同的结果吗? - pietrodito
@pietrodito GKi使用另一个版本的R。是的,这很有用,可以避免复制。我用这种方法来更改一个大矩阵的属性,否则R会抛出错误“无法分配...”。 - Stéphane Laurent
我的意思是:在某些情况下,R复制数据框是否有用?我也使用R 4.0.3并且会进行复制... 这里有一些我们不理解或者没有明确指出的地方。 - pietrodito
3
在R语言中,复制-on-修改是最常见的复制模式;如果你想进行修改而不进行复制(称之为“浅复制”,可变对象,引用访问等),你需要特别注意(在编译的{C,C ++}代码内进行更改,或使用data.tableset *功能,或使用环境或引用类)。虽然复制-on-修改可能会浪费空间(如果您确实不需要保持原始对象不变),但它比引用访问更安全,后者需要用户自己确保不会意外修改对象。 - Ben Bolker
寻找3.6.3和4.0.x之间R的变化是很有趣的。我简要查看了NEWS文件,但没有找到相关内容。(我认为这样的更改将在此文件中列出,而不是在任何旧的NEWS文件中,因为3.6.3是4.0.0之前的最后一个版本。)由于这是一个相当微妙的差异,它可能已经作为其他事情的副作用而被更改,并且没有在NEWS中明确说明... - Ben Bolker
1
@Ben Bolker:我有R 4.0.3并且已经复制了一份。我猜GKi的答案可能有问题。 - pietrodito

2

在我的情况下,它并不是在复制数据:

m <- matrix(1:12, nrow = 3)
.Internal(inspect(m))
#@250ff98 13 INTSXP g0c4 [REF(1),ATT] (len=12, tl=0) 1,2,3,4,5,...
#ATTRIB:
#  @38da270 02 LISTSXP g0c0 [REF(1)] 
#    TAG: @194d610 01 SYMSXP g0c0 [MARK,REF(1171),LCK,gp=0x4000] "dim" (has value)
#    @38c3d88 13 INTSXP g0c1 [REF(65535)] (len=2, tl=0) 3,4

attr(m, "dim") <- 4:3
.Internal(inspect(m))
#@250ff98 13 INTSXP g0c4 [REF(1),ATT] (len=12, tl=0) 1,2,3,4,5,...
#ATTRIB:
#  @38da270 02 LISTSXP g0c0 [REF(1)] 
#    TAG: @194d610 01 SYMSXP g0c0 [MARK,REF(1171),LCK,gp=0x4000] "dim" (has value)
#    @38d9978 13 INTSXP g0c0 [REF(65535)]  4 : 3 (expanded)

这是@250ff98,并且之后仍然存在。它只是将dim@38c3d88更改为@38d9978

sessionInfo()
#R version 4.0.3 (2020-10-10)
#Platform: x86_64-pc-linux-gnu (64-bit)
#Running under: Debian GNU/Linux 10 (buster)
#
#Matrix products: default
#BLAS:   /usr/local/lib/R/lib/libRblas.so
#LAPACK: /usr/local/lib/R/lib/libRlapack.so
#
#locale:
# [1] LC_CTYPE=en_GB.UTF-8       LC_NUMERIC=C              
# [3] LC_TIME=en_GB.UTF-8        LC_COLLATE=en_GB.UTF-8    
# [5] LC_MONETARY=en_GB.UTF-8    LC_MESSAGES=en_GB.UTF-8   
# [7] LC_PAPER=en_GB.UTF-8       LC_NAME=C                 
# [9] LC_ADDRESS=C               LC_TELEPHONE=C            
#[11] LC_MEASUREMENT=en_GB.UTF-8 LC_IDENTIFICATION=C       
#
#attached base packages:
#[1] stats     graphics  grDevices utils     datasets  methods   base     
#
#loaded via a namespace (and not attached):
#[1] compiler_4.0.3 tools_4.0.3   

与`tracemem`相同。
m <- matrix(1:12, nrow = 3)
tracemem(m)
#[1] "<0x289ff98>"
attr(m, "dim") <- 4:3
tracemem(m)
#[1] "<0x289ff98>"

但是,如果你在其中加入一个str(m),它就会复制一份:

m <- matrix(1:12, nrow = 3)
tracemem(m)
#[1] "<0x28a01c8>"
str(m)
# int [1:3, 1:4] 1 2 3 4 5 6 7 8 9 10 ...
attr(m, "dim") <- 4:3
#tracemem[0x28a01c8 -> 0x2895608]: 

你能解释一下 inspect 的输出吗? - pietrodito
不幸的是,我没有找到关于inspect的文档,而.Internal则表示:“只有真正的R巫师才应该考虑使用此函数...”。我正在使用它,因为只有在使用了--enable-memory-profiling编译R时,tracemem才能工作。我希望以@开头的所有内容都是一个地址。 - GKi
你看到了吗?我使用相同版本的R,但结果与你的不同。 - pietrodito
是的,所以我问了:“为什么在一台电脑上R会进行复制修改,而在另一台电脑上却不会呢?”(原文链接:https://dev59.com/p1IG5IYBdhLWcg3whQSH) - GKi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接