Напишите свои собственные функции tidyselect - PullRequest
3 голосов
/ 13 июня 2019

Я написал пакет R, который использует селекторы {tidyselect} (например, contains(), starts_with() и т. Д.). Я хотел бы добавить еще несколько вспомогательных функций select в пакет для выбора переменных на основе некоторого атрибута. Например, выберите все числовые переменные или, возможно, все логические переменные.

Я просмотрел базовый код {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 Ответ

2 голосов
/ 13 июня 2019

Когда вы вызываете 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 средой .

Необработанная реализация может выглядеть примерно так:

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

Созданона 2019-06-13 по пакету представ (v0.2.1)

...