将参数传递给dplyr函数

asn*_*snr 50 r lazy-evaluation dplyr

我想使用下面的计算参数化dplyr,找出哪些值Sepal.Length与多个值相关联Sepal.Width:

library(dplyr)

iris %>%
    group_by(Sepal.Length) %>%
    summarise(n.uniq=n_distinct(Sepal.Width)) %>%
    filter(n.uniq > 1)
Run Code Online (Sandbox Code Playgroud)

通常我会写这样的东西:

not.uniq.per.group <- function(data, group.var, uniq.var) {
    iris %>%
        group_by(group.var) %>%
        summarise(n.uniq=n_distinct(uniq.var)) %>%
        filter(n.uniq > 1)
}
Run Code Online (Sandbox Code Playgroud)

但是,这种方法会因为dplyr使用非标准评估而引发错误.应该如何编写这个函数?

asn*_*snr 48

您需要使用dplyr函数的标准评估版本(只需将'_'附加到函数名称,即.group_by_&summarise_)并将字符串传递给您的函数,然后您需要将其转换为符号.要参数化summarise_的参数,您需要使用interp(),它在lazyeval包中定义.具体来说:

library(dplyr)
library(lazyeval)

not.uniq.per.group <- function(df, grp.var, uniq.var) {
    df %>%
        group_by_(grp.var) %>%
        summarise_( n_uniq=interp(~n_distinct(v), v=as.name(uniq.var)) ) %>%
        filter(n_uniq > 1)
}

not.uniq.per.group(iris, "Sepal.Length", "Sepal.Width")
Run Code Online (Sandbox Code Playgroud)

有关详细信息,请参阅非标准评估的dplyr 插图.


Pau*_*aul 18

与最高0.5的旧dplyr版本一样,新的dplyr具有标准评估(SE)和非标准评估(NSE)的功能.但他们的表达方式与之前不同.

如果您需要NSE功能,则传递裸表达式并使用enquo将其捕获为quosures.如果您想要SE功能,则省略sym()并直接传递quosures(或符号).以下是该问题的SE解决方案:

library(tidyverse)
library(rlang)

f1 <- function(df, grp.var, uniq.var) {
   df %>%
       group_by(!!grp.var) %>%
       summarise(n_uniq = n_distinct(!!uniq.var)) %>%
       filter(n_uniq > 1)  
}

a <- f1(iris, quo(Sepal.Length), quo(Sepal.Width))
b <- f1(iris, sym("Sepal.Length"), sym("Sepal.Width"))
identical(a, b)
#> [1] TRUE
Run Code Online (Sandbox Code Playgroud)

请注意SE版本如何使您能够使用字符串参数 - 首先使用它们将它们转换为符号sym().有关更多信息,请参阅使用dplyr vignette 编程.


Moo*_*per 15

这是使用curl卷曲 {{伪运算符从rlang 0.4执行此操作的方法:

library(dplyr)

not.uniq.per.group <- function(data, group.var, uniq.var) {
  data %>%
    group_by({{group.var}}) %>%
    summarise(n.uniq=n_distinct({{uniq.var}})) %>%
    filter(n.uniq > 1)
}

iris %>% not.uniq.per.group(Sepal.Length, Sepal.Width)
#> # A tibble: 25 x 2
#>    Sepal.Length n.uniq
#>           <dbl>  <int>
#>  1          4.4      3
#>  2          4.6      4
#>  3          4.8      3
#>  4          4.9      5
#>  5          5        8
#>  6          5.1      6
#>  7          5.2      4
#>  8          5.4      4
#>  9          5.5      6
#> 10          5.6      5
#> # ... with 15 more rows
Run Code Online (Sandbox Code Playgroud)


akr*_*run 11

在devel版本dplyr(即将发布0.6.0)中,我们也可以使用稍微不同的语法来传递变量.

f1 <- function(df, grp.var, uniq.var) {
   grp.var <- enquo(grp.var)
   uniq.var <- enquo(uniq.var)

   df %>%
       group_by(!!grp.var) %>%
       summarise(n_uniq = n_distinct(!!uniq.var)) %>%
       filter(n_uniq >1)  


}

res2 <- f1(iris, Sepal.Length, Sepal.Width) 
res1 <- not.uniq.per.group(iris, "Sepal.Length", "Sepal.Width")
identical(res1, res2)
#[1] TRUE
Run Code Online (Sandbox Code Playgroud)

这里enquo接受参数并quosure通过懒惰地评估函数参数并在汇总内部将值返回为(类似于基本R中的替换),我们要求它取消引用(!!或UQ)以便对其进行评估.


Sør*_*ein 6

dplyr(0.7.4)的当前版本中,不建议使用标准评估函数版本(例如,在函数名称后附加“ _” group_by_)。相反,编写函数时应依靠tidyeval

这是函数的外观示例:

# definition of your function
not.uniq.per.group <- function(data, group.var, uniq.var) {
  # enquotes variables to be used with dplyr-functions
  group.var <- enquo(group.var)
  uniq.var <- enquo(uniq.var)

  # use '!!' before parameter names in dplyr-functions
  data %>%
    group_by(!!group.var) %>%
    summarise(n.uniq=n_distinct(!!uniq.var)) %>%
    filter(n.uniq > 1)
}

# call of your function
not.uniq.per.group(iris, Sepal.Length, Sepal.Width)
Run Code Online (Sandbox Code Playgroud)

如果您想了解所有细节,有一个极好的小插曲由dplyr队在这如何工作的。