【问题标题】:What is the difference between . and .data?和有什么区别。和.data?
【发布时间】:2020-12-03 12:42:44
【问题描述】:

我正在尝试更深入地理解将点(“.”)与dplyr 一起使用以及将.data 代词与dplyr 一起使用。我写这篇文章的代码是这样的:

cat_table <- tibble(
  variable = vector("character"), 
  category = vector("numeric"), 
  n        = vector("numeric")
) 

for(i in c("cyl", "vs", "am")) {
  cat_stats <- mtcars %>% 
    count(.data[[i]]) %>% 
    mutate(variable = names(.)[1]) %>%
    rename(category = 1)
  
  cat_table <- bind_rows(cat_table, cat_stats)
}
# A tibble: 7 x 3
  variable category     n
  <chr>       <dbl> <dbl>
1 cyl             4    11
2 cyl             6     7
3 cyl             8    14
4 vs              0    18
5 vs              1    14
6 am              0    19
7 am              1    13

代码做了我想让它做的事情,并不是这个问题的重点。我只是为上下文提供它。

我正在尝试更深入地了解为什么它会做我想让它做的事情。更具体地说,为什么我不能交替使用..data。我已经阅读了Programming with dplyr 文章,但我想在我看来,..data 都只是表示“我们的结果在管道中的这一点”。但是,我似乎过于简化了我对它们如何工作的心理模型,因为当我在下面的names() 中使用.data 时出现错误:

mtcars %>% 
  count(.data[["cyl"]]) %>% 
  mutate(variable = names(.data)[1])
Error: Problem with `mutate()` input `variable`.
x Can't take the `names()` of the `.data` pronoun
ℹ Input `variable` is `names(.data)[1]`.
Run `rlang::last_error()` to see where the error occurred.

当我在count() 内部使用. 时,我得到了一个意想不到的(对我来说)结果:

mtcars %>% 
  count(.[["cyl"]]) %>% 
  mutate(variable = names(.)[1])
  .[["cyl"]]  n   variable
1          4 11 .[["cyl"]]
2          6  7 .[["cyl"]]
3          8 14 .[["cyl"]]

我怀疑它与“请注意,.data 不是数据框;它是一个特殊的结构,代词,它允许您直接访问当前变量,使用 .data$x 或间接使用.data[[var]]。不要指望其他函数可以使用它,”来自 Programming with dplyr 文章。这告诉我.data 不是 - 一个数据框 - 但是,我仍然不确定.data 是什么以及它与@有何不同987654341@.

我试着像这样弄清楚:

mtcars %>% 
  count(.data[["cyl"]]) %>% 
  mutate(variable = list(.data))

但是,结果&lt;S3: rlang_data_pronoun&gt; 对我没有任何帮助我理解的意义。如果有人对此有更好的了解,我将不胜感激。谢谢!

【问题讨论】:

    标签: r dplyr tidyeval


    【解决方案1】:

    在前面,我认为.data 的意图有点令人困惑,直到人们还考虑它的兄弟代词.env

    .magrittr::%&gt;% 设置和使用的东西;因为dplyr 重新导出它,它就在那里。每当你引用它时,它就是一个真实的对象,所以names(.)nrow(.) 等都按预期工作。它确实反映了管道中到目前为止的数据。

    另一方面,.datarlang 中定义,目的是消除符号解析的歧义。与.env 一起,它可以让您非常清楚地知道您希望在哪里解决特定符号(当预期有歧义时)。来自?.data,我认为这是一个鲜明的对比:

    disp <- 10
    mtcars %>% mutate(disp = .data$disp * .env$disp)
    mtcars %>% mutate(disp = disp * disp)
    

    然而,正如帮助页面中所述,.data(和.env)只是一个“代词”(我们有动词,所以现在我们也有代词),所以它只是一个指针来解释应解析符号的整洁内部结构。这只是一种暗示。

    所以你的陈述

    ..data 都只是表示“我们在管道中的这一点的结果。”

    不正确:. 表示到目前为止的数据,.data 只是对内部的声明性提示。


    考虑另一种思考.data的方式:假设我们有两个函数可以完全消除引用符号的环境歧义:

    • get_internally,此符号必须始终引用列名,如果该列不存在,它将不会到达封闭环境;和
    • get_externally,此符号必须始终引用封闭环境中的变量/对象,它永远不会匹配列。

    在这种情况下,翻译上面的例子,人们可能会使用

    disp <- 10
    mtcars %>%
      mutate(disp = get_internally(disp) * get_externally(disp))
    

    在这种情况下,get_internally 不是框架似乎更明显,因此您不能调用names(get_internally) 并期望它做一些有意义的事情(NULL 除外)。就像names(mutate)

    所以不要将.data 视为一个对象,将其视为一种消除符号环境歧义的机制。我认为它使用的$ 既简洁/易于使用又绝对具有误导性:它不是list-like 或environment-like 对象,即使它被视为这样。

    顺便说一句:可以为 $ 编写任何 S3 方法,使任何分类对象看起来像一个框架/环境:

    `$.quux` <- function(x, nm) paste0("hello, ", nm, "!")
    obj <- structure(0, class = "quux")
    obj$r2evans
    # [1] "hello, r2evans!"
    names(obj)
    # NULL
    

    $ 访问器的存在并不总是意味着对象是框架/环境。)

    【讨论】:

    • 谢谢@r2evans。但是,我仍然不能 100% 确定为什么 mutate(variable = names(.data)[1]) 会引发错误。对此有何见解?
    • 再次感谢您。我仍然不确定我是否完全吸收了这一点。但是,我比以前更接近了。
    • "为什么 names(.data) 会引发错误。"那是因为.data 不是数据框而是环境(见我的回答)。 @r2evans 不错的答案,但我认为它缺少 .data 始终代表 current 数据的关键点。特别是,它表示当前组的数据,而不是整个数据框,它还将包含在以前的表达式中创建的新列。
    • 更准确地说,它具有环境(实际上是环境链)的数据结构,而不是数据框。这是一个具有自己的访问器方法的代词,它表示一个环境。
    • 关于(1),是因为底层包含了tidy eval的特殊绑定。在一些更复杂的情况下(tidyselect),我们将函数存储在上层,数据存储在下层。关于(2),我们是故意这样做的,因为环境本质上是无序的,这与数据帧不同。允许names() 会令人困惑并导致编程错误。
    【解决方案2】:

    比较 mtcars %&gt;% count(.data[["cyl"]])mtcars %&gt;% count(.[["cyl"]])

    mtcars %>% count(.[["cyl"]])
      .[["cyl"]]  n
    1          4 11
    2          6  7
    3          8 14
    
    
    mtcars %>% count(.data[["cyl"]])
      cyl  n
    1   4 11
    2   6  7
    3   8 14
    

    . 实际上就是前面的结果。所以第一个类似于:

    . <- mtcars
    count(., .[["cyl"]])
    

    第二个是通过字符串“cyl”查找变量并将之前的结果作为变量的搜索路径的简写。例如,假设您拼错了变量名:

    mtcars %>% count(.[["cyll"]])
       n
    1 32
    
    mtcars %>% count(.data[["cyll"]])
    Error: Must group by variables found in `.data`.
    * Column `cyll` is not found.
    

    使用. 不会引发错误,因为索引到不存在的列是返回NULL 的有效base-R 操作。

    使用.data 会抛出,因为使用了不存在的变量:

    mtcars %>% count(cyll)
    

    也抛出。

    【讨论】:

    • 谢谢你,@thc。但是,我仍然不能 100% 确定为什么 mutate(variable = names(.data)[1]) 会引发错误。对此有何见解?
    • @BradCannell 因为.data 不是一个对象,并且从未在内部进行评估。它只是一个关键字(他们称之为代词),它提供了一种使用方括号 [[$ 表示法访问列的方法。关键概念是“惰性评估”。 R 中函数中的参数在您使用它们之前不会被评估,并且有很多方法可以在不评估的情况下查看参数。在内部,.data 从不用作对象。它被简单地视为指导函数执行某些操作的参数。
    【解决方案3】:

    . 变量来自magrittr,与管道有关。它的意思是“被输送到这个表达式中的值”。通常使用管道时,前一个表达式的值会成为下一个表达式中的参数 1,但这为您提供了一种在其他参数中使用它的方法。

    .data 对象是 dplyr 的特殊对象(尽管它在 rlang 包中实现)。它本身没有任何有用的价值,但是当在dplyr“tidy eval”框架中进行评估时,它在许多方面的作用就好像它是数据框/tibble 的值一样。当有歧义时使用它:如果你有一个与数据框列同名的变量foo,那么.data$foo 说它是你想要的列(如果找不到它会给出错误,不像data$foo这将给NULL)。您也可以使用.env$foo,表示忽略列并从调用环境中获取变量。

    .data.env 都特定于 dplyr 函数和其他使用相同特殊评估方案的函数,而 . 是常规变量,可用于任何函数。

    编辑添加:您问为什么names(.data) 不起作用。如果@r2evans 出色的答案还不够,这里有一个不同的看法:我怀疑问题是names() 不是dplyr 函数,即使names.rlang_fake_data_pronounrlang 中的一个方法。所以表达式names(.data) 是使用常规评估而不是整洁评估来评估的。该方法不知道要查看哪个数据框,因为在那种情况下没有。

    【讨论】:

    • 谢谢@user2554330。但是,我仍然不能 100% 确定为什么 mutate(variable = names(.data)[1]) 会引发错误。对此有何见解?
    • 查看补充内容。
    • 不错的上下文。您的最后一段表明,如果他们实现(例如)names.rlang_fake_data_pronoun,他们可能能够使 that 功能正常工作。 (它仍然没有解决我 itch 使用 .data 的问题,就像我使用 .SD ... :-)
    • 其实有一个names.rlang_fake_data_pronoun 方法:它就是打印错误信息的地方。它不能做任何其他事情,因为它是在常规的 R 评估方案中评估的,而不是 tidy eval。 Tidy eval 可以访问大量dplyr 特定的信息。
    【解决方案4】:

    在理论层面上:

    . 是 magrittr 代词。它表示通过%&gt;% 管道输入的整个输入(与 dplyr 一起使用时通常是一个数据帧)。

    .data 是整洁的 eval 代词。从技术上讲,它根本不是一个数据框,而是一个评估环境。

    在实践层面上:

    . 永远不会被 dplyr 修改。它保持不变,直到到达下一个管道表达式。另一方面,.data 始终是最新的。这意味着您可以引用以前创建的变量:

    mtcars %>%
      mutate(
        cyl2 = cyl + 1,
        am3 = .data[["cyl2"]] + 10
      )
    

    而且你也可以在分组数据框的情况下参考列slices

    mtcars %>%
      group_by(cyl) %>%
      mutate(cyl2 = .data[["cyl"]] + 1)
    

    如果您改用.[["cyl"]],整个数据框将被子集化,并且您会收到错误,因为输入大小与组切片大小不同。棘手!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-11
      • 2018-06-18
      • 2010-10-04
      • 2018-06-11
      • 2016-03-26
      • 2019-10-12
      相关资源
      最近更新 更多