使用带引号的参数作为闭包实例化的变量名?
Use quoted parameter as variable name for closure instantiation?
我已将我的大部分问题作为评论写在我提供的代表中。我希望改进我的代码的语义并回答有关将引用变量作为 closure-like 函数的参数的特定问题。
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
此函数清除 'target' 测试基名。
所有 test-names 都以其 parent-dir 名称和句点开头。
(例如 'last.test5')
这个函数接受一个 "target" 列和任意数量的
parent-dir 列。它反转 parent-dirs 的列表和
找到第一个 non-NA 值。然后将该值匹配到
目标值并将其删除。
我的问题在于这个函数:
- 有没有更语义化的方法来构建这个函数
以便它可以在 `mutate()' 函数中表达?
目前,replace_pattern()
函数依赖于
.key
列标题为 "target" 和
被硬编码为输入参数。
这是因为 `pmap' 的工作方式是 p-num
列表中的参数和名称的匹配参数。
因为我希望这个函数适用于任意深度
file-paths,我需要找到一种方法来处理不同的 .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
由 reprex package (v0.3.0)
于 2020-04-08 创建
回答问题 1
当您说您通常看不到 dat
传递给 mutate()
时,那是因为大多数函数通常不需要数据框上下文。例如,当您看到
mtcars %>% mutate( cyl = sqrt(cyl) )
函数 sqrt()
直接处理传递给它的值,而不关心这些值的来源。在您的情况下,您需要数据框上下文来帮助解析 ref01:ref03
表达式。出于这个原因,更合适的模式是将 mutate()
操作 放在 您的函数中,并让它 return 生成数据框。
回答问题 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
的 shorthand)用于单数列。新功能的使用方法如下:
dat %>% mutate_target( target, ref01:ref03 ) # Works
dat %>% rename( abc = target ) %>% mutate_target( abc, ref01:ref03 ) # Also works
我已将我的大部分问题作为评论写在我提供的代表中。我希望改进我的代码的语义并回答有关将引用变量作为 closure-like 函数的参数的特定问题。
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
此函数清除 'target' 测试基名。 所有 test-names 都以其 parent-dir 名称和句点开头。 (例如 'last.test5')
这个函数接受一个 "target" 列和任意数量的 parent-dir 列。它反转 parent-dirs 的列表和 找到第一个 non-NA 值。然后将该值匹配到 目标值并将其删除。
我的问题在于这个函数:
- 有没有更语义化的方法来构建这个函数 以便它可以在 `mutate()' 函数中表达?
目前,
replace_pattern()
函数依赖于.key
列标题为 "target" 和 被硬编码为输入参数。这是因为 `pmap' 的工作方式是 p-num 列表中的参数和名称的匹配参数。
因为我希望这个函数适用于任意深度 file-paths,我需要找到一种方法来处理不同的
.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
由 reprex package (v0.3.0)
于 2020-04-08 创建回答问题 1
当您说您通常看不到 dat
传递给 mutate()
时,那是因为大多数函数通常不需要数据框上下文。例如,当您看到
mtcars %>% mutate( cyl = sqrt(cyl) )
函数 sqrt()
直接处理传递给它的值,而不关心这些值的来源。在您的情况下,您需要数据框上下文来帮助解析 ref01:ref03
表达式。出于这个原因,更合适的模式是将 mutate()
操作 放在 您的函数中,并让它 return 生成数据框。
回答问题 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
的 shorthand)用于单数列。新功能的使用方法如下:
dat %>% mutate_target( target, ref01:ref03 ) # Works
dat %>% rename( abc = target ) %>% mutate_target( abc, ref01:ref03 ) # Also works