【问题标题】:curly curly tidy evaluation programming with multiple inputs and custom function across columnscurly curly 整洁的评估编程,具有跨列的多个输入和自定义函数
【发布时间】:2020-11-25 04:25:31
【问题描述】:

我的问题类似于this question,但我需要跨列应用更复杂的函数,但我不知道如何将 Lionel 建议的解决方案应用于具有范围动词(如 filter_at() 或 @)的自定义函数987654323@+across() 等价。好像没有引入“superstache”/{{{}}} 运算符。

这是我想做的一个非编程示例(不使用 NSE):

library(dplyr)
library(magrittr)

foo <- tibble(group = c(1,1,2,2,3,3),
              a = c(1,1,0,1,2,2),
              b = c(1,1,2,2,0,1))

foo %>%
  group_by(group) %>%
  filter_at(vars(a,b), any_vars(n_distinct(.) != 1)) %>%
  ungroup
#> # A tibble: 4 x 3
#>   group     a     b
#>   <dbl> <dbl> <dbl>
#> 1     2     0     2
#> 2     2     1     2
#> 3     3     2     0
#> 4     3     2     1

我还没有找到与 filter+across() 等效的 filter_at 行,但由于新的(ish)tidyeval 函数早于 dplyr 1.0,我认为可以搁置这个问题。这是我尝试制作一个程序版本,其中过滤变量由用户提供,带有点:

my_function <- function(data, ..., by) {
  dots <- enquos(..., .named = TRUE)
  
  helperfunc <- function(arg) {
    return(any_vars(n_distinct(arg) != length(arg)))
  }
  
  dots <- lapply(dots, function(dot) call("helperfunc", dot))
  
  data %>%
    group_by({{ by }}) %>%
    filter(!!!dots) %>%
    ungroup
}

foo %>%
  my_function(a, b, group)
#> Error: Problem with `filter()` input `..1`.
#> x Input `..1` is named.
#> i This usually means that you've used `=` instead of `==`.
#> i Did you mean `a == helperfunc(a)`?

如果有一种方法可以在 filter_atvars() 参数中插入 NSE 运算符,而不必进行所有这些额外的调用,我会很高兴(我假设这是 {{{}}} 函数会做吗?)

【问题讨论】:

    标签: r dplyr rlang tidyeval non-standard-evaluation


    【解决方案1】:

    这是一种使用across() 来实现vignette("colwise") 中介绍的方法。

    my_function <- function(data, vars, by) {
      
      data %>%
        group_by({{ by }}) %>%
        filter(n_distinct(across({{ vars }}, ~ .x)) != 1) %>%
        ungroup()
      
    }
     
    foo %>%
      my_function(c(a, b), by = group)
    
    # A tibble: 4 x 3
      group     a     b
      <dbl> <dbl> <dbl>
    1     2     0     2
    2     2     1     2
    3     3     2     0
    4     3     2     1
    

    【讨论】:

    • 其实我觉得~.x &gt; 0不太对。尝试-foo %&gt;% my_function(c(a, b), by = group)(即,否定数据框中的所有值)。
    【解决方案2】:

    也许我误解了问题所在,但forwarding the dots 的标准模式在这里似乎可以正常工作:

    my_function <- function(data, ..., by) {
      data %>%
        group_by({{ by }}) %>%
        filter_at(vars(...), any_vars(n_distinct(.) != 1)) %>%
        ungroup
    }
    
    foo %>%
      my_function( a, b, by=group )     # works
    

    【讨论】:

    • 没有意识到这里需要ensyms——谢谢。我很难找到关于 NSE 函数的最新指南(quoenexprs、``quo_name, as_name` 等)。编程小插曲过去有更多关于此的内容,但现在似乎主要是关于卷曲的。跟踪术语也很困难——似乎他们正在从“引用”和“解离”等术语转向“间接”、“掩蔽”和“拥抱”。但也许我把事情搞混了。
    • 同意@MrFlick 的观点,across() 解决方案也很有趣
    • @MrFlick 我不确定这里是否有直接的across() 等价物。莱昂内尔可能会纠正我,但我很确定across() 一次只处理一列。如果自定义函数需要对多个列进行操作,我可能会先通过nest() 对列进行分组。
    • @lost 请查看编辑。事实证明这里甚至不需要ensyms(),因为你可以转发这些点。一个好的最新资源可能是Tidy evaluation book
    • @MrFlick 我不认为问题出在across().cols 参数上,而是.fns 中提供的函数一次操作一列。 any_vars()specifically a filter_at() construct,这是等效性中断的地方。
    【解决方案3】:

    across 的选项

    my_function <- function(data, by, ...) {
     
      dots <- enquos(..., .named = TRUE)
      nm1 <- purrr::map_chr(dots, rlang::as_label) 
         
         
      data %>%
        dplyr::group_by({{ by }}) %>%
        dplyr::mutate(across(nm1, ~ n_distinct(.) !=1, .names = "{col}_ind")) %>%
        dplyr::ungroup() %>% 
        dplyr::filter(dplyr::select(., ends_with('ind')) %>% purrr::reduce(`|`)) %>%
        dplyr::select(-ends_with('ind'))
        
        
    }
    
    my_function(foo, group, a, b)
    # A tibble: 4 x 3
    #  group     a     b
    #  <dbl> <dbl> <dbl>
    #1     2     0     2
    #2     2     1     2
    #3     3     2     0
    #4     3     2     1
    

    filter/across

    foo %>%
       group_by(group) %>%
       filter(any(!across(c(a,b), ~ n_distinct(.) == 1)))
    # A tibble: 4 x 3
    # Groups:   group [2]
    #  group     a     b
    #  <dbl> <dbl> <dbl>
    #1     2     0     2
    #2     2     1     2
    #3     3     2     0
    #4     3     2     1
    

    【讨论】:

      猜你喜欢
      • 2017-11-16
      • 1970-01-01
      • 1970-01-01
      • 2020-12-06
      • 2020-02-06
      • 2021-11-25
      • 1970-01-01
      • 2021-02-25
      • 2018-06-10
      相关资源
      最近更新 更多