在闭包实例化中使用引号参数作为变量名?

问题描述 投票:2回答:1

我已经把我的大部分问题写成了我提供的reprex中的注释,我希望改进我的代码的语义,并回答关于在闭包类函数中使用引号变量作为参数的具体问题。我想改进我的代码语义,并回答一个关于使用引号变量作为类似闭包函数的参数的具体问题。

library(tidyverse)

# A df of file-paths split so all basenames
# are in the same column, but parent-dirs
# are spread across an abritary number of columns
# and filled with NA's.
dat <- tibble(
  ref01 = rep("analysis", 5),
  ref02 = c(NA, NA, "next", "next", "next"),
  ref03 = c(NA, NA, NA, NA, "last"),
  target = c("analysis.test1", "analysis.test2",
             "next.test3", "next.test4",
             "last.test5")
)

# For example this reprex df shows file-paths
# from a file-tree that looks like:
# analysis
# ├── next
# │   ├── last
# │   │   └── last.test5
# │   ├── next.test3
# │   └── next.test4
# ├── analysis.test1
# └── analysis.test2
dat
#> # A tibble: 5 x 4
#>   ref01    ref02 ref03 target        
#>   <chr>    <chr> <chr> <chr>         
#> 1 analysis <NA>  <NA>  analysis.test1
#> 2 analysis <NA>  <NA>  analysis.test2
#> 3 analysis next  <NA>  next.test3    
#> 4 analysis next  <NA>  next.test4    
#> 5 analysis next  last  last.test5

这个函数清理了'目标'的测试基名,所有的测试名前面都有它的父目录名和句号(例如'last.test5')。

这个函数接收一个 "目标 "列和任意数量的父目录。它反转父目录列表,找到第一个非NA值。然后将该值与目标值进行匹配并将其删除。

我的问题就在这个函数中。

  1. 有没有一种更符合语义的方式来构建这个函数 使它可以在 "mutate() "函数中表达出来?
  2. 目前 replace_pattern() 函数依靠的是 .key 列的标题是 "target",并且是硬编码的输入参数。

    这是因为 "pmap "的工作方式是从列表中获取p-num参数,并将参数与名称匹配。

    由于我想让这个函数适用于任意深度的文件路径,我需要找到一种方法来处理不同的文件路径。.key 名字。

    有没有办法引用 .key 变量的名称,这样它就会成为 replace_pattern() 函数?

trim_target <- function(.tbl, .key, ...){
  key <- tidyselect::eval_select(expr(c(!!enquo(.key))), .tbl)
  loc <- tidyselect::eval_select(expr(c(...)), .tbl)

  # First param has to be "target" since that's the name
  # of the .key column.
  replace_pattern <- function(target, ...){
    args <- c(...)
    pattern <- args %>% 
      rev() %>% 
      discard(is.na) %>% 
      first() %>% 
      paste0("\\.")

    unlist(str_remove(target, pattern))
  }

  pmap(.tbl[,c(key, loc)], replace_pattern) %>% 
    unlist()
}

预期输出:这和预期的一样,但不能扩展。另外,参考问题01,我必须通过 dat 进入 mutate() function-call;我没有看到通常这样做。

dat %>% 
  mutate(target = trim_target(dat, target, ref01:ref03))
#> # A tibble: 5 x 4
#>   ref01    ref02 ref03 target
#>   <chr>    <chr> <chr> <chr> 
#> 1 analysis <NA>  <NA>  test1 
#> 2 analysis <NA>  <NA>  test2 
#> 3 analysis next  <NA>  test3 
#> 4 analysis next  <NA>  test4 
#> 5 analysis next  last  test5

创建于2020-04-08,由 重读包 (v0.3.0)

r dplyr purrr rlang tidyeval
1个回答
0
投票

回答问题1

当你说你通常不会看到。dat 传给 mutate()这是因为大多数函数通常不需要数据框架上下文。例如,当你看到

mtcars %>% mutate( cyl = sqrt(cyl) )

作用 sqrt() 直接使用传递给它的值,而不关心这些值的来源。在你的例子中,你需要数据框架上下文来帮助解决这个问题。ref01:ref03 的表达式。出于这个原因,更合适的模式是将 mutate() 运作 里面 并让它返回结果数据帧。

回答问题2

pmap() 只有当输入是一个命名的列表时,才会匹配参数名。如果列表是未命名的,则按位置进行匹配。所以,你可以选择 1) 不命名参数列表。

.tbl[,c(key, loc)] %>% as.list() %>% unname %>% pmap_chr(replace_pattern)

或者 2) 因为你已经用以下方法对你的列进行了分组 [,把它变成一个适当的 select 模式,并相应地重命名所选列。

.tbl %>% select( target={{.key}}, ... ) %>% pmap_chr( replace_pattern )

把所有的东西整合在一起

考虑到这两点,我将重写你的函数。

mutate_target <- function(.tbl, .key, ...){

  # No change from the OP
  replace_pattern <- function(target, ...){
    args <- c(...)
    pattern <- args %>%
      rev() %>%
      discard(is.na) %>%
      first() %>%
      paste0("\\.")

    unlist(str_remove(target, pattern))
  }

  result <- .tbl %>% select( target={{.key}}, ... ) %>% pmap_chr( replace_pattern )
  .tbl %>% mutate( {{.key}} := result )
}

注意,我把你的显式 eval_select() 调用。你可以通过 ... 点直接用于dplyr动词,而使用卷曲-卷曲( {{缩写为 !!enquo )的单列。下面是如何使用新函数。

dat %>% mutate_target( target, ref01:ref03 )                           # Works
dat %>% rename( abc = target ) %>% mutate_target( abc, ref01:ref03 )   # Also works
© www.soinside.com 2019 - 2024. All rights reserved.