编写自己的 tidyselect 函数
Write your own tidyselect functions
我写了一个 R 包,它利用了 {tidyselect} select 或(例如 contains()
、starts_with()
等)。我想根据某些属性向 select 变量的包中添加更多 select 辅助函数。例如,select 所有数值变量或可能所有逻辑变量。
我已经查看了 {tidyselect} 基本代码。但我无法推测变量的注册是如何工作的,因此无法通过属性将其扩展到 select 个变量。
我进行了一些搜索,看起来 {recipes} 包已经成功实现了我正在寻找的其他帮助器(例如 all_numeric()
),但我正在努力自己编写扩展函数。 https://github.com/tidymodels/recipes/blob/master/R/selections.R
归根结底,我相信,我不明白当变量注册到 tidyselect::scoped_vars()
函数时发生了什么。如果我 运行 tidyselect::scoped_vars(vars = names(mtcars))
在干净的环境中,我看不到任何更改。但是我可以在注册变量后在全局环境中使用 {tidyselect} 助手。
names(mtcars)
#> [1] "mpg" "cyl" "disp" "hp" "drat" "wt" "qsec" "vs" "am" "gear"
#> [11] "carb"
tidyselect::scoped_vars(vars = names(mtcars))
# returns position of column 'mpg'
tidyselect::starts_with("mp")
#> 1
对于某些文档的任何提示或指导将不胜感激!谢谢!
当您调用 scoped_vars()
时,给定的变量名称在当前函数调用期间保存在内部环境中:
(function() {
print(tidyselect:::vars_env$selected)
tidyselect::scoped_vars(names(mtcars))
print(tidyselect:::vars_env$selected)
})()
#> NULL
#> [1] "mpg" "cyl" "disp" "hp" "drat" "wt" "qsec" "vs" "am" "gear"
#> [11] "carb"
print(tidyselect:::vars_env$selected)
#> NULL
据我所知,这是 {tidyselect} 保留的关于变量的唯一信息;所以如果要select基于属性,就得自己维护属性信息。这也是 {recipes} 所做的,带有 cur_info_env
environment.
粗略的实现可能如下所示:
type_env <- rlang::new_environment()
select_with_attributes <- function(.data, ...) {
type_env$types <- purrr::map(.data, class)
dplyr::select(.data, ...)
}
all_numeric <- function() {
which(purrr::map_lgl(type_env$types, ~ any(.x %in% "numeric")))
}
head(select_with_attributes(iris, all_numeric()))
#> Sepal.Length Sepal.Width Petal.Length Petal.Width
#> 1 5.1 3.5 1.4 0.2
#> 2 4.9 3.0 1.4 0.2
#> 3 4.7 3.2 1.3 0.2
#> 4 4.6 3.1 1.5 0.2
#> 5 5.0 3.6 1.4 0.2
#> 6 5.4 3.9 1.7 0.4
由 reprex package (v0.2.1)
于 2019-06-13 创建
我写了一个 R 包,它利用了 {tidyselect} select 或(例如 contains()
、starts_with()
等)。我想根据某些属性向 select 变量的包中添加更多 select 辅助函数。例如,select 所有数值变量或可能所有逻辑变量。
我已经查看了 {tidyselect} 基本代码。但我无法推测变量的注册是如何工作的,因此无法通过属性将其扩展到 select 个变量。
我进行了一些搜索,看起来 {recipes} 包已经成功实现了我正在寻找的其他帮助器(例如 all_numeric()
),但我正在努力自己编写扩展函数。 https://github.com/tidymodels/recipes/blob/master/R/selections.R
归根结底,我相信,我不明白当变量注册到 tidyselect::scoped_vars()
函数时发生了什么。如果我 运行 tidyselect::scoped_vars(vars = names(mtcars))
在干净的环境中,我看不到任何更改。但是我可以在注册变量后在全局环境中使用 {tidyselect} 助手。
names(mtcars)
#> [1] "mpg" "cyl" "disp" "hp" "drat" "wt" "qsec" "vs" "am" "gear"
#> [11] "carb"
tidyselect::scoped_vars(vars = names(mtcars))
# returns position of column 'mpg'
tidyselect::starts_with("mp")
#> 1
对于某些文档的任何提示或指导将不胜感激!谢谢!
当您调用 scoped_vars()
时,给定的变量名称在当前函数调用期间保存在内部环境中:
(function() {
print(tidyselect:::vars_env$selected)
tidyselect::scoped_vars(names(mtcars))
print(tidyselect:::vars_env$selected)
})()
#> NULL
#> [1] "mpg" "cyl" "disp" "hp" "drat" "wt" "qsec" "vs" "am" "gear"
#> [11] "carb"
print(tidyselect:::vars_env$selected)
#> NULL
据我所知,这是 {tidyselect} 保留的关于变量的唯一信息;所以如果要select基于属性,就得自己维护属性信息。这也是 {recipes} 所做的,带有 cur_info_env
environment.
粗略的实现可能如下所示:
type_env <- rlang::new_environment()
select_with_attributes <- function(.data, ...) {
type_env$types <- purrr::map(.data, class)
dplyr::select(.data, ...)
}
all_numeric <- function() {
which(purrr::map_lgl(type_env$types, ~ any(.x %in% "numeric")))
}
head(select_with_attributes(iris, all_numeric()))
#> Sepal.Length Sepal.Width Petal.Length Petal.Width
#> 1 5.1 3.5 1.4 0.2
#> 2 4.9 3.0 1.4 0.2
#> 3 4.7 3.2 1.3 0.2
#> 4 4.6 3.1 1.5 0.2
#> 5 5.0 3.6 1.4 0.2
#> 6 5.4 3.9 1.7 0.4
由 reprex package (v0.2.1)
于 2019-06-13 创建