编写自己的tidyselect函数

时间:2019-06-13 13:57:34

标签: r tidyselect r-recipes

我写了一个使用{tidyselect}选择器(例如contains()starts_with()等)的R包。我想在包中添加一些选择帮助器函数,以基于某些属性选择变量。例如,选择所有数字变量或所有逻辑变量。

我已经查看了{tidyselect}基本代码。但是我无法推测变量的注册是如何工作的,因此无法将其扩展为通过变量的属性选择变量。

我已经进行了一些搜索,看起来{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

任何提示或一些文档说明将不胜感激!谢谢!

1 个答案:

答案 0 :(得分:2)

调用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}保留的有关变量的唯一信息;因此,如果要基于属性进行选择,则必须自己维护属性信息。 {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创建