【问题标题】:Why does item assignment in non-existant data.frame column work?为什么不存在的 data.frame 列中的项目分配有效?
【发布时间】:2011-06-22 09:32:29
【问题描述】:

灵感来自Q6437164:有人可以向我解释为什么以下工作有效:

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

对我来说,R 将这条语句解释为:在 data.frame 中创建一个名为 someNonExistantColumn 的新列,并将第一个值(实际上,所有值,看起来)设置为值 15。

【问题讨论】:

    标签: r


    【解决方案1】:

    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
    

    这里,函数是递归计算的。首先提取器函数"$"用于从iris访问组件"foo",即NULL

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

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

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

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

    > 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?答案在?"$&lt;-.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
    

    使用"$&lt;-"()"[&lt;-"()"$"() 进行正确的函数调用。 R 语言定义的Section 3.4.4 中的示例使用了这个更简单的示例:

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

    计算结果为

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

    这更容易理解,因为names() 看起来像一个普通的函数调用。

    【讨论】:

    • 这简直是邪恶的美丽。我很高兴我问了,我很高兴你回答了。你刚刚把我从精神疾病中救了出来。
    【解决方案2】:

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

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

    iris$newcol <- 1
    

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

    当表达式的计算结果为 NULL 时,会触发新列的创建。来自?$&lt;-

    • “当 $

    所以我认为这里发生的情况是表达式的计算结果为 NULL,这会触发代码创建一个新列,然后使用向量循环来填充值。

    编辑

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

    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
    

    【讨论】:

    • 实际上,我真的不明白这个(我的原始代码)是如何在 R 中解析的。是 bracketassign(dollar(iriscopy, someNonExistantColumn), 1, 15) 吗?还是dollarassign(iriscopy, bracket(someNonExistantColumn, 1), 15)? (请原谅这里的愚蠢命名)。还是我错误地假设所有内容最终都被解析为函数调用?
    • @NickSabbe 我在这里远远超出了我的舒适区,但我试图在我的编辑中回答你的问题。
    • 我不认为你在那儿打电话给[&lt;-。查看args('[&lt;-.data.frame'),您会发现需要指定ijvalue,并且ij 可以为空,就像在提取时将它们留空一样。所以head('[&lt;-'(iris, , 6, 2)) 有效,但您不能命名该列,因为它不存在。
    • @Gavin:你能就我原来的问题提供一些意见吗?我在这里让自己发疯,试图解释 R 是如何解析 iriscopy$someNonExistantColumn[1]&lt;-15 的。@Andrie 对什么有一些想法,但不是真的(还)为什么。
    • @Nick Sabbe 看到我刚刚提交的答案。这让我的大脑受伤,但我想我已经深究了。很难,所以需要一段时间才能正确解释。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多