-5

我不太清楚为什么数据框对象不更新

d <- data.frame(titi=c(0))
(function(dataset) {
  dataset[["toto"]] <- 1;
  print(names(dataset)) #has "toto" and "titi"
})(d)
print(names(d)) # no has "toto", only "titi" 

这里发生了什么 ?

我有一个解决方法,因为在我的代码中我还捕获了变量并使用 更新捕获的<<-,但我想知道机制。

我知道一般突变的危险等等。我只是不明白这里的机制。

编辑

虽然这似乎是一个语言级别的特性的共识,但我不遵循这个论点,好像我使用了一个封闭的结构,数据表,它可以变异:

d <- data.table(titi=c(0))
(function(dataset) {
  dataset[,toto:=1]
  print(names(dataset)) #"titi" "toto"
})(d)
print(names(d)) #"titi" "toto"
4

3 回答 3

8

这里发生了什么?

你还没有读过R 的介绍,尤其是关于函数内赋值的部分或关于编写你自己的函数的部分

两个相关的引用是

请注意,在函数内完成的任何普通赋值都是局部的和临时的,并且在退出函数后会丢失。

表达式的值是函数返回的值。

在您的情况下,函数调用的最终值names将返回一个字符向量....


范围界定可能是一个复杂的问题,但您的示例是一个简单的案例。

如果您想要更完整的参考,请查看R 语言定义


但是data.table有没有....

是的,:=data.table通过引用分配。这不是普通的任务

data.table继承自data.frame。它们不相同,并且:=通过引用分配(也setattr)通过引用分配。这违背了标准的R成语。这也可能导致问题,请参阅Why does data.table update names(DT) by reference, 即使我分配给另一个变量?

还有其他解决方法,但标准R习惯用法是函数内的普通赋值是局部的和临时的,并且在 exit 之后丢失

您也可以考虑使用ReferenceClasses(见?setRefClass


它不仅限于列表/data.frames。原子向量是一样的

  mydf <- data.frame(a=1)
  mylist <- list(a=1)
  mynumeric <- c(a = 1)

mydf <- data.frame(a=1)
mylist <- list(a=1)
mynumeric <- c(a = 1)

foo <- function(x){x[['b']] <- 1; print(names(x))}

# data.frame
foo(mydf)
# [1] "a" "b"
mydf
#    a
#  1 1

# list
foo(mylist)
# [1] "a" "b"
mylist
# $a
# [1] 1

# atomic
foo(mynumeric)
# [1] "a" "b"
 mynumeric
# a 
#  1 
于 2013-08-13T00:36:17.983 回答
3

下面的代码实现了您的预期,即使用匿名函数似乎更改了“按引用”的列表参数:

d <- data.frame(titi=c(0))
(function(dataset) { nam <-deparse(substitute(dataset))
   dataset[["toto"]] <- 1; assign(nam ,  dataset, envir=parent.frame() )
   print(names(dataset)) #has "toto" and "titi"
 })(d)
#[1] "titi" "toto"
 print(names(d))
#[1] "titi" "toto"
 d
#  titi toto
#1    0    1

我不认为您会发现它比以下内容更容易或更有效:

d <- data.frame(titi=c(0))
 addcol <- function(dataset) { 
   dataset[["toto"]] <- 1
   dataset}
d <-addcol(d)
d
#  titi toto
#1    0    1
于 2013-08-13T02:15:27.837 回答
3

做类似的事情

foo <- data.frame(x=2)
(function(df) {df[["x"]] <- 1; df})(foo)

没有将返回的值分配给任何东西,并且发现foo结果没有改变,这实际上并没有什么不同

x <- 2
(function(n) {n <- 1; n})(x)

并发现那x是不变的。为什么你会期待别的东西是一个很好的问题。


我认为这将是关于 R 的更常见(和合理)的问题之一,即:为什么从本地环境修改驻留在外部环境中的数据集,没有做任何事情。也就是说,给定以下代码

foo <- data.frame(x=2)
(function() {foo[["x"]] <- 1; foo})()

为什么foo还是不变?

在这种情况下,考虑

df[[x]] <- y

作为语法糖

df <- data.frame(df[names(df)!="x"], x = y)

因为毕竟,在函数式语言中你没有副作用,修改对象的一部分是副作用。换句话说,您实际上是在制作df具有所需更改的新副本。

与任何分配一样,这发生在本地环境中。如果原件df也恰好位于此环境中,则新副本将替换旧副本。如果没有,那么您现在有两个版本df:一个在本地环境中,一个在其他地方。如果您没有将新版本分配给任何东西,那么函数返回时它将丢失。

于 2013-08-13T08:12:22.560 回答