使用 dplyr 在自定义函数中无法识别默认参数

Posted

技术标签:

【中文标题】使用 dplyr 在自定义函数中无法识别默认参数【英文标题】:default arguments not being recognized in custom function using dplyr 【发布时间】:2021-11-05 14:00:48 【问题描述】:

使用这个函数foo()。我希望它有一个默认参数cyl,因为这是它通常会处理的字段的名称。

library(tidyverse)

foo <- function(x = cyl)
    case_when(
        x == 6 ~ TRUE,
        x == 8 ~ FALSE,
        x == 4 ~ NA
    )


# works: 
mtcars %>% 
    mutate(cyl_refactor = foo(cyl)) %>% 
    select(cyl, cyl_refactor)

但令我惊讶的是,除非我明确提供默认参数,否则该函数将无法工作。请参阅下面的失败代码

# fails:
mtcars %>% 
    mutate(cyl_refactor = foo()) %>% 
    select(cyl, cyl_refactor)

Error: Problem with `mutate()` column `cyl_refactor`. ℹ `cyl_refactor = foo()`. x object 'cyl' not found

似乎只有当还有如下数据参数时才会处理默认参数。

foo2 <- function(data, x = cyl)
    data %>% 
        mutate(cyl_refactor = case_when(
        x == 6 ~ TRUE,
        x == 8 ~ FALSE,
        x == 4 ~ NA
    ))


mtcars %>% 
    foo2() %>% 
    select(cyl, cyl_refactor)

我确信我在准引用方面的知识存在一些差距,但我想了解如何在 foo() 中使用默认参数。

【问题讨论】:

这显然只是示例代码,有没有办法为 foo() 提供默认参数?我是否需要使用带引号的列名,如果需要,case_when() 可以处理带引号的列名吗? 在我的实际用例中,最好避免使用数据参数 所以当我尝试 sym() 方法时出现错误:只有字符串可以转换为符号 retain_92dv2 duration & is.na(!!cancel_date) ~ TRUE, # 烘焙 + 不取消 = 保留 !!cancel_date - !!gns_date > duration ~ TRUE, !!cancel_date - !!gns_date 是这个主意吗?传递带引号的参数,然后在函数中使用 arg 【参考方案1】:

虽然我不推荐,但这是一个“有效”的方法

foo <- function(x = cyl)
  x <- enquo(x)
  eval.parent(rlang::quo_squash(rlang::quo(case_when(
    !!x == 6 ~ TRUE,
    !!x == 8 ~ FALSE,
    !!x == 4 ~ NA
  ))))


# Both run without error
mtcars %>% 
  mutate(cyl_refactor = foo(cyl)) %>% 
  select(cyl, cyl_refactor)

mtcars %>% 
  mutate(cyl_refactor = foo()) %>% 
  select(cyl, cyl_refactor)

问题在于,为了让case_when 工作,您不能只传入列名而不传入数据。为了在这种情况下“找到”数据,我使用eval.parent() 沿着调用链向上尝试找到cyl 变量。

最好在直接传入输入数据的地方创建适当的函数(而不是他们需要自己查找的变量名)。

【讨论】:

所以听起来好像在没有数据参数的自定义函数中使用 case_when() 对于它无法“找到”的默认参数会有问题 @Joe 当您不直接将数据作为参数传递时,对于任何函数来说都是一个问题。 case_when 没什么特别的。如果函数的默认参数是变量名而不是实际值,那么如果该变量未在函数中定义并且仅存在于调用环境中,则会出现问题。这与 R 使用词法作用域查找变量的正常行为相冲突。【参考方案2】:

我们可以使用 missingcur_data_all 来做到这一点

foo <- function(x = cyl)
   if(missing(x)) x <- cur_data_all()[["cyl"]]
   
    case_when(
        x == 6 ~ TRUE,
        x == 8 ~ FALSE,
        x == 4 ~ NA
    )

-测试

> out1 <- mtcars %>% 
+     mutate(cyl_refactor = foo(cyl)) %>% 
+     select(cyl, cyl_refactor)
> out2 <- mtcars %>% 
+     mutate(cyl_refactor = foo()) %>% 
+     select(cyl, cyl_refactor)
> 
> identical(out1, out2)
[1] TRUE

【讨论】:

以上是关于使用 dplyr 在自定义函数中无法识别默认参数的主要内容,如果未能解决你的问题,请参考以下文章

在自定义函数中使用 SQL 查询作为数组参数(输入)

R:在自编写的包中使用magrittr管道运算符

如何在自定义 R 函数中使用文本字符串作为变量

如何在自定义 cmdlet 中正确使用 -verbose 和 -debug 参数

尝试在自定义 UIView 子类上使用点击识别器时出现 NSUncaughtException

dplyr:连接到外部数据库时在 summarise() 中使用自定义函数