【问题标题】:Functions that return a Mutable or Immutable variables in R [duplicate]在R中返回可变或不可变变量的函数[重复]
【发布时间】:2017-02-17 11:26:26
【问题描述】:

我认为这个问题与 R 中的 Mutable Vs Immutable 对象的概念有关,它可能是一个“初学者问题”。我在names() 和包data.tablesetnames() 函数中遇到了这个问题。我确信这是预期的行为,但对我来说这非常令人惊讶,而且我确信这不仅与 names() 有关。

假设我有一个名为 dt 的 data.table,其中包含两列 a 和 b:

dt <- data.table(a = 1:5, b= 1:5)
oldNames <- names(dt)

如果你打印oldNames,它显然会显示:

oldNames
[1] "a" "b"

但是如果你把dt的名字改成setnames():

setnames(dt,oldNames,c("aNew","bNew"))

变量oldNames的内容发生了变化。

oldNames
[1] "aNew" "bNew"

我知道在 Python 中,这是某些数据类型(可变数据类型)的预期行为,而不是其他数据类型(不可变数据类型)的预期行为。在R中,是否也存在这种二分法?

对我来说,“预期”的行为是变量 oldNames 存储列的名称,它不依赖于 data.table 的未来变化。例如,length() 函数不会发生这种情况:

L <- length(dt)
L
[1] 2
dt[,c:=1:5]
L
[1] 2

非常感谢任何指向有关此行为或解释的一些好的信息的链接以及编码方式,以便oldNames 在 dt 修改后不会更改其内容。

【问题讨论】:

  • 你是对的,它是重复的,我认为这是一个 R 问题/行为,通常不是 Data.Table。非常感谢。

标签: r data.table immutability mutable


【解决方案1】:

我相信这是由于 data.table 包的实现。在 R 中,多个符号可以指向同一个东西,但通常不会引起问题,因为对象会在修改时被复制,例如:

a <- c(1,2)
b <- a
# To check the memory address,
# `a` and `b` are pointed to the same object since `b` does not modify `a`.
pryr::address(a)
[1] "0x1a735620"
pryr::address(b)
[1] "0x1a735620"
# Then we modify `a`
a <- c(1,3)
# We will notice that the address of `a` has changed
# since there are modifications, but `b` not.
pryr::address(a)
[1] "0x1a72f168"
pryr::address(b)
[1] "0x1a735620"

据我所知,data.table 包有点特殊,因为它会通过一些操作来修改对象。见:

dt <- data.table(a = 1:5, b= 1:5)
n1 <- names(dt)

pryr::address(n1)
[1] "0x18aeffe0"

setnames(dt, c("a","b"), c("aa","bb"))

n2 <- names(dt)
pryr::address(n2) # identical to the address of `n1`
[1] "0x18aeffe0"

n1
[1] "aa" "bb"

我认为data.table包没有识别出有一个变量指向了它的name属性,从而导致了这个问题。我认为这是一个错误,您可能希望用“data.table”标记问题。

在当前时间,可以使用n &lt;- c(names(dt))来存储名字,这样R会认为c()修改了name属性,并将其存储在不同的内存地址中。

顺便说一句,R 确实有可变对象,请参阅 Reference classR6 objects;-)

问候;

更新:

参见 ?data.table::copy 和 ?data.table::setnames

引用 ?data.table::copy:

执行“dt_names = names(DT)”时可能需要“copy()”。由于 R 的 copy-on-modify,'dt_names' 仍然指向相同的位置 内存为“名称(DT)”。因此现在修改'DT'引用, 比如说通过添加一个新列,“dt_names”也会得到更新。避免 对此,必须明确复制:'dt_names

它们在R中当然不常见,data.table可以做到这一点,因为它使用了R的C接口。

会话信息:

> sessionInfo()
R version 3.3.2 (2016-10-31)
Platform: x86_64-suse-linux-gnu (64-bit)
Running under: openSUSE Tumbleweed

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=en_US.UTF-8       
 [4] LC_COLLATE=en_US.UTF-8     LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
 [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                  LC_ADDRESS=C              
[10] LC_TELEPHONE=C             LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

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

other attached packages:
[1] pryr_0.1.2          data.table_1.9.6    magrittr_1.5        personalutils_0.1.0

loaded via a namespace (and not attached):
[1] tools_3.3.2      Rcpp_0.12.9      stringi_1.1.2    codetools_0.2-15
[5] stringr_1.1.0    chron_2.3-47  

【讨论】:

  • 非常感谢您的回答。当您说“data.table 包没有识别出指向其名称属性的变量,从而导致问题”时,是因为这是 R 包中的常见行为吗?
  • 非常感谢您的回答。当您说“data.table 包没有识别出指向其名称属性的变量,从而导致问题”时,这是 R 包中的常见行为吗?实际上,我对您的地址洞察力有些困惑。我检查了L (length()) 对象的地址,它的地址没有改变(就像names 对象一样),但是当我将新列添加到dt 时它的值没有'也不改变!所以L 指向内存中的相同位置,但它的值不会从 2 变为 3。
  • 嘿 Gonzalo,对不起,我在示例中犯了一个错误,您可以查看我的更新答案。使用length()names()有点不同,使用length()就像使用c(names())一样,会做一些计算并生成一个新的变量。
  • 这种行为并不常见,正如您最初预期的那样。
  • 非常感谢 Marlin,您对 data.table 文档的引用是我一直在寻找的答案。我没能找到它,因为我认为问题不在于 data.table,而在于一般的 R。
猜你喜欢
  • 2015-06-22
  • 2019-03-30
  • 2021-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-28
  • 2020-10-30
相关资源
最近更新 更多