【问题标题】:Vectorizing a tidyverse function with `...`使用 `...` 向量化 tidyverse 函数
【发布时间】:2021-11-12 19:35:57
【问题描述】:

下面的函数 foo 将数字列 var1 重新编码为字符串列(1 --> "a"2 --> "b" 等)。

我想知道如何对这个函数进行矢量化,这样我们就有了参数...,而不是参数...

也就是说,... 中提供的所有变量都应与var1foo 中一样。

可能有用的帖子可能是HERE

library(tidyverse)

foo <- function(data, var1, caps = FALSE, reverse = FALSE){
  
let <-  if(caps) base::LETTERS else base::letters
FUN <- if(reverse) utils::tail else utils::head

data %>% mutate(var1 = factor(FUN(let, max(var1))[var1]))  
}

# EXAMPLE OF USE:
(dat <- data.frame(var1 = c(2,1,3,1,4:1), id = 1:8))

foo(dat, var1, caps = T, reverse = T)

【问题讨论】:

    标签: r function tidyverse vectorization rlang


    【解决方案1】:

    下面的方法应该可以满足您的需求。它不像我对您问题中链接帖子的回答那么干净,但它应该可以按预期工作(现在还使用适用于所有变量的默认参数进行了更新):

    library(tidyverse)
    library(rlang)
    
    foo <- function(data, ..., caps = FALSE, reverse = FALSE){
      
      var_ls <- map(ensyms(..., .named = TRUE), as.character)
      l_varls <- length(var_ls) 
      
      if(l_varls != length(caps)) {
        caps <- rep(caps, l_varls)
      }
      if(l_varls != length(reverse)) {
        reverse <- rep(reverse, l_varls)
      }
      
      let <- ifelse(caps, list(base::LETTERS), list(base::letters))
      names(let) <- names(var_ls)
      FUN <- ifelse(reverse, list(utils::tail), list(utils::head))
      names(FUN) <- names(var_ls)
      
      mutate(data,
             purrr::map_dfc(var_ls,
                            ~ factor(FUN[[.x]](let[[.x]],max(data[[.x]]))[data[[.x]]]))
             )
      
    }
    
    # EXAMPLE OF USE:
    (dat <- data.frame(var1 = c(2,1,3,1,4:1), var2 = c(8:1), id = 1:8))
    #>   var1 var2 id
    #> 1    2    8  1
    #> 2    1    7  2
    #> 3    3    6  3
    #> 4    1    5  4
    #> 5    4    4  5
    #> 6    3    3  6
    #> 7    2    2  7
    #> 8    1    1  8
    
    foo(dat, var1, var2, caps = c(TRUE,FALSE), reverse = c(FALSE, TRUE))
    #>   var1 var2 id
    #> 1    B    z  1
    #> 2    A    y  2
    #> 3    C    x  3
    #> 4    A    w  4
    #> 5    D    v  5
    #> 6    C    u  6
    #> 7    B    t  7
    #> 8    A    s  8
    foo(dat, var1, var2)
    #>   var1 var2 id
    #> 1    b    h  1
    #> 2    a    g  2
    #> 3    c    f  3
    #> 4    a    e  4
    #> 5    d    d  5
    #> 6    c    c  6
    #> 7    b    b  7
    #> 8    a    a  8
    foo(dat, var1, var2, caps = TRUE, reverse = TRUE)
    #>   var1 var2 id
    #> 1    X    Z  1
    #> 2    W    Y  2
    #> 3    Y    X  3
    #> 4    W    W  4
    #> 5    Z    V  5
    #> 6    Y    U  6
    #> 7    X    T  7
    #> 8    W    S  8
    

    reprex package (v0.3.0) 于 2021-09-18 创建

    【讨论】:

    • @SimonHarmel:我更新了我的答案,现在包括默认参数,这些参数将自动扩展到所有指定的变量。根据输入列的数量更改输出需要一些思考,并且可能值得提出一个新问题。
    • 亲爱的蒂姆,我相信this question 也是您的理想选择。
    【解决方案2】:

    我们可以使用across -

    foo <- function(data, caps = FALSE, reverse = FALSE, ...){
      vars <- rlang::ensyms(...) 
      let <-  if(caps) base::LETTERS else base::letters
      FUN <- if(reverse) utils::tail else utils::head
      
      data %>% mutate(across(as.character(vars), ~factor(FUN(let, max(.))[.])))  
    }
    
    dat <- data.frame(var1 = c(2,1,3,1,4:1), id = 1:8)
    foo(dat, caps = T, reverse = T, var1)
    
    #  var1 id
    #1    X  1
    #2    W  2
    #3    Y  3
    #4    W  4
    #5    Z  5
    #6    Y  6
    #7    X  7
    #8    W  8
    
    foo(dat, caps = T, reverse = T, var1, id)
    
    #  var1 id
    #1    X  S
    #2    W  T
    #3    Y  U
    #4    W  V
    #5    Z  W
    #6    Y  X
    #7    X  Y
    #8    W  Z
    

    【讨论】:

    • 我认为该帖子与此处无关。在那里,您将命名值传递给函数而不是列名。如果您想通过 ... 不带引号尝试更新的答案。
    • 当然可以,但是capsreverse 的参数没有向量化,有没有办法让foo(dat, caps = c(T,F), reverse = c(F,T), var1, id) 工作?
    • 你可以在across(c(...), ~中使用...
    猜你喜欢
    • 2021-05-09
    • 1970-01-01
    • 2021-07-29
    • 1970-01-01
    • 2018-05-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多