我已经把我的大部分问题写成了我提供的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值。然后将该值与目标值进行匹配并将其删除。
我的问题就在这个函数中。
目前 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)
回答问题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