为什么不存在的data.frame列中的项目分配有效?

时间:2011-06-22 09:32:29

标签: r

Q6437164的启发:有人可以向我解释为什么以下有效:

iriscopy<-iris #or whatever other data.frame
iriscopy$someNonExistantColumn[1]<-15

对我来说,似乎并不明显R如何将此语句解释为:在data.frame中创建一个名为someNonExistantColumn的新列,并设置第一个值(事实上,所有值,如图所示)值15。

2 个答案:

答案 0 :(得分:10)

R语言定义手册为我们提供了一个指针,指向R如何计算表单的表达式:

x$foo[1] <- 15

即好像我们已经打电话了

`*tmp*` <- x
x <- "$<-.data.frame"(`*tmp*`, name = "foo", 
                      value = "[<-.data.frame"("$.data.frame"(`*tmp*`, "foo"), 
                                               1, value = 15))
rm(`*tmp*`)
如果我们为了说明的目的而放弃使用的实际方法,那么中间位可能更容易解决:

x <- "$<-"(`*tmp*`, name = "foo", 
           value = "[<-"("$"(`*tmp*`, "foo"), 1, value = 15))

要使用iris回到您的示例,我们有类似

的内容
iris$foo[1] <- 15

这里,递归地评估函数。首先,提取器函数"$"用于从"foo"访问组件iris,即NULL

> "$"(iris, "foo")
NULL

然后,"[<-"用于替换上面返回的对象的第一个元素(NULL),其值为15,即调用:

> "[<-"(NULL, 1, value = 15)
[1] 15

现在,这是在我们调用的最外层用作参数value的对象,即使用"$<-"的赋值:

> head("$<-"(iris, "foo", value = 15))
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species foo
1          5.1         3.5          1.4         0.2  setosa  15
2          4.9         3.0          1.4         0.2  setosa  15
3          4.7         3.2          1.3         0.2  setosa  15
4          4.6         3.1          1.5         0.2  setosa  15
5          5.0         3.6          1.4         0.2  setosa  15
6          5.4         3.9          1.7         0.4  setosa  15

(此处包含在head()中以限制显示的行数。)

希望能够解释函数如何调用进度。要处理的最后一个问题是为什么整个向量foo设置为15? ?"$<-.data.frame"的详细信息部分给出了答案:

Details:

....

         Note that there is no ‘data.frame’ method for ‘$’, so ‘x$name’
     uses the default method which treats ‘x’ as a list.  There is a
     replacement method which checks ‘value’ for the correct number of
     rows, and replicates it if necessary.

关键位是最后一句。在上面的示例中,最外面的分配使用了value = 15。但此时,我们希望替换整个组件"foo",其长度为nrow(iris)。因此,在最外面的赋值/函数调用中实际使用的是value = rep(15, nrow(iris))

这个例子更加复杂,因为你必须从

的便利符号转换
x$foo[1] <- 15

使用"$<-"()"[<-"()"$"()进行正确的函数调用。 The R Language Definition的Section 3.4.4中的示例使用了这个更简单的示例:

names(x)[3] <- "Three"

评估为

`*tmp*` <- x
x <- "names<-"(`*tmp*`, value="[<-"(names(`*tmp*`), 3, value="Three"))
rm(`*tmp*`)

稍微容易理解,因为names()看起来像是通常的函数调用。

答案 1 :(得分:3)

我认为答案是它不起作用。

我认为$newcol是创建新列的标准行为。例如:

iris$newcol <- 1

将在iris data.frame中创建一个新列。由于矢量回收,所有值都将为1。

当表达式求值为NULL时,将触发此新列的创建。来自?$<-

  • “当$&lt; - 应用于NULL x时,它首先将x强制转换为list()。这也是[[&lt; - 如果替换值的长度大于1的话] :如果值的长度为1或0,则x首先被强制转换为值类型的零长度向量。“

所以我认为这里发生的是表达式求值为NULL,这会触发代码创建一个新列,然后使用向量回收来填充值。

修改

解析可能使用$ -assign $<-而不是括号分配[<-。比较:

head(`$<-`(iris, newcol, 1))
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species newcol
1          5.1         3.5          1.4         0.2  setosa      1
2          4.9         3.0          1.4         0.2  setosa      1
3          4.7         3.2          1.3         0.2  setosa      1
4          4.6         3.1          1.5         0.2  setosa      1
5          5.0         3.6          1.4         0.2  setosa      1
6          5.4         3.9          1.7         0.4  setosa      1

但括号分配会产生错误:

head(`[<-`(iris, newcol, 1))
Error in head(`[<-`(iris, newcol, 1)) : 
  error in evaluating the argument 'x' in selecting a method for function 'head': Error in is.atomic(value) : 'value' is missing