dplyr中波浪符和点符号表示法的含义是什么?

3

概要

我正在阅读一篇关于dplyr的across函数的文章。在查看第一个使用示例时,我发现了我从未见过的运算符。我不知道它们是否是dplyr固有的还是来自其他包。无论哪种情况,我都不明白代码中它们的用法。

代码示例:

starwars |>
    summarize(across(where(is.character), ~ length(unique(.x))))

结果是一个1 x 8的tibble。
我理解across的第一个参数,但第二个参数让我困惑。什么是~length(unique(.x))?代码中的".x"是什么意思?我知道length被应用到tibble中的每个字符向量上,但是"unique"函数对这段代码有什么作用? 我为解决这个问题尝试了什么? 我尝试使用谷歌搜索[R] ~运算符,但没有找到相关结果。我还尝试过rdrr.io、r-project.org和CRAN,但都没有解决。我还查看了tidyverse.org和purrr文档,因为看到其他人在使用相同语法时提到了purrr。 问题: 有人能帮我理解内部发生了什么吗?

这些帖子可能会有所帮助 - https://dev59.com/L6_la4cB1Zd3GeqPvZpz 和 https://dev59.com/U1cP5IYBdhLWcg3wH24Z - Ronak Shah
2个回答

10

这被称为 purr-style lambda,以波浪符 ~ 开头,并使用 .x 来引用在 .cols 参数中选择的每个单独的列。所以:

# We can either use
starwars |> summarize(across(where(is.character), ~ length(unique(.x)))) 

# Or we can define our anonymous function like this
starwars |> summarize(across(where(is.character), function(x) length(unique(x)))) 

它们是等效的。但需要注意的是,您指定了要应用于starwars数据集中每一列的函数,该数据集的类别为character。 为此,在此代码中使用了where函数,它是一个辅助函数,将一个函数(这里是is.character)应用于仅选择那些类别为character的列。 然后我们在每个列上应用我们的匿名函数,并将结果保存在一个单独的列中。所以这里的.x代表每一列的类别为character。 只需注意,|>是R的新创建的本地管道运算符。
通常我们在tidyverse包中使用%>%,但两者都完成了相同的工作,即将其LHS的结果替换为其RHS的第一个参数,该参数通常(在这种情况下也是如此)是.data参数。 还有一件事要提到的是,尽管它在下面用于另一个目的。为了理解后端如何解释lambda-style-formula,我们可以使用as_mapper,它是大多数purrr函数允许的各种函数规范背后的强大引擎。
library(purrr)

> as_mapper(~ length(unique(.x)))
<lambda>
function (..., .x = ..1, .y = ..2, . = ..1) 
length(unique(.x))
attr(,"class")
[1] "rlang_lambda_function" "function" 

如果你注意它的输出,你会意识到它被解释为我们通常使用的匿名函数。
更多信息请阅读this文档。

1
因此,对于我的tibble中的每个字符向量变量,都会应用unique函数。如果在我的tibble starwars中有三个字符向量变量:a、b和c,那么如果a定义如下: a <- c("equivalent", "but", "for", "then", "so", "just", "we", "for") 那么unique(a)将返回字符向量[1] "equivalent" "but" "for" "then" "so" "just" "we",其中a的最后一个元素"for"不会重复。因此,length(unique(a))返回一个整数向量,计算a中每个成员字符串中的字符数。所以,length(unique(a))的结果是[1] 10 3 3 4 2 4 2。对吗? - student-R
是的,确切无误。 - Anoushiravan R
将@jpdugo提出的答案与此答案结合考虑是个好主意。您可以始终将purrr-style-lambda公式放入对as_mapper的调用中,以查看其在后端如何被解释为匿名函数。 - Anoushiravan R

4

~是在tidyverse中编写lambda函数的一种快捷方式。 ~length(unique(.x))会产生与function(.x) {length(unique(.x))}相同的结果。

这种编写函数的方式最可能用于purrr库,事实上,purrr::as_mapper()识别此语法并返回一个可以像其他R函数一样调用的函数。

例如:函数f将告诉我.x有多少个唯一值。也可以称为...1。如果您有两个参数,则为.x.y。最后,对于n个参数,为..1..2..3 .... ..n

require(purrr)
#> Loading required package: purrr

f <- as_mapper(~length(unique(.x)))

f2 <- as_mapper(~length(unique(.)))

f3 <- as_mapper(~length(unique(..1)))
  
f(mtcars$mpg) 
#> [1] 25
f2(mtcars$mpg)
#> [1] 25
f3(mtcars$mpg)
#> [1] 25

这是由 reprex 包 (v2.0.0) 在2021年07月05日创建的。

另一个示例:

library(dplyr)
library(purrr)

#in all starwars columns that are character vectors, how many unique values are they?

starwars |>
  summarize(across(where(is.character), ~ length(unique(.x))))
#> # A tibble: 1 x 8
#>    name hair_color skin_color eye_color   sex gender homeworld species
#>   <int>      <int>      <int>     <int> <int>  <int>     <int>   <int>
#> 1    87         13         31        15     5      3        49      38

f <- as_mapper(~ if (is.character(.x)) length(unique(.x)) else NULL)

f_base <- function(.x) {if (is.character(.x)) length(unique(.x)) else NULL}

starwars %>% 
  map_dfc(f)
#> # A tibble: 1 x 8
#>    name hair_color skin_color eye_color   sex gender homeworld species
#>   <int>      <int>      <int>     <int> <int>  <int>     <int>   <int>
#> 1    87         13         31        15     5      3        49      38

starwars %>% 
  map_dfc(f_base)
#> # A tibble: 1 x 8
#>    name hair_color skin_color eye_color   sex gender homeworld species
#>   <int>      <int>      <int>     <int> <int>  <int>     <int>   <int>
#> 1    87         13         31        15     5      3        49      38

本示例由reprex包(v2.0.0)于2021年07月05日创建。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接