Как я могу зафиксировать изменения, внесенные в data.frame в dplyr :: select?
Я хотел бы создать подкласс data.frame
который несет некоторую информацию о состоянии определенных столбцов. Я думал, что лучший способ сделать это - использовать атрибут,special_col
. Кажется, что простой конструктор работает нормально:
# Light class that keeps an attribute about a particular special column
new_my_class <- function(x, special_col) {
stopifnot(inherits(x, "data.frame"))
attr(x, "special_col") <- special_col
class(x) <- c("my_class", class(x))
x
}
my_mtcars <- new_my_class(mtcars, "mpg")
class(my_mtcars) # subclass of data.frame
#> [1] "my_class" "data.frame"
attributes(my_mtcars)$special_col # special_col attribute is still there
#> $special_col
#> [1] "mpg"
Однако я столкнулся с проблемой, заключающейся в том, что мне нужно написать методы для различных обобщений, чтобы обновить этот атрибут, если имя столбца изменится. Как показано ниже, используяdata.frame
оставит атрибут нетронутым.
library(dplyr)
# Using select to rename a column does not update the attribute
select(my_mtcars, x = mpg) %>%
attr("special_col")
#> [1] "mpg"
Вот моя нынешняя наивная попытка найти способ my_class
. Я собираю точки, а затем анализирую их, чтобы выяснить, какие столбцы были переименованы, и меняю атрибут, если они действительно были переименованы.
# I attempt to capture the dots supplied to select and replace the attribute
select.my_class <- function(.data, ...) {
exprs <- enquos(...)
sel <- NextMethod("select", .data)
replace_renamed_cols(sel, "special_col", exprs)
}
# This is slightly more complex than needed here in case there is more than one special_col
replace_renamed_cols <- function(x, which, exprs) {
att <- attr(x, which)
renamed <- nzchar(names(exprs)) # Bool: was column renamed?
old_names <- purrr::map_chr(exprs, rlang::as_name)[renamed]
new_names <- names(exprs)[renamed]
att_rn_idx <- match(att, old_names) # Which attribute columns were renamed?
att[att_rn_idx] <- new_names[att_rn_idx]
attr(x, which) <- att
x
}
# This solves the immmediate problem:
select(my_mtcars, x = mpg) %>%
attr("special_col")
#> [1] "x"
К сожалению, я думаю, что это особенно хрупко и не работает в других обстоятельствах, как показано ниже.
# However, this fails with other expressions:
select(my_mtcars, -cyl)
#> Error: Can't convert a call to a string
select(my_mtcars, starts_with("c"))
#> Error: Can't convert a call to a string
Я считаю, что было бы предпочтительнее получать изменения в столбцах после tidyselect
выполнил свою работу, вместо того, чтобы пытаться генерировать те же изменения атрибутов из захваченных точек, как это сделал я. Ключевой вопрос: как я могу использоватьtidyselect
инструменты, чтобы понять, какие изменения произойдут с фреймом данных при выборе переменных?. В идеале я мог бы вернуть что-то, что отслеживает, какие столбцы переименованы, в какие другие, какие отброшены и т.д., и использовать это для сохранения атрибутаspecial_col
своевременно.
1 ответ
Я думаю, что способ сделать это - закодировать обновление атрибутов в [
а также names<-
методы, то метод выбора по умолчанию должен использовать эти универсальные типы. Так должно быть в следующей основной версии dplyr.
См. https://github.com/r-lib/tidyselect/blob/8d1c76dae81eb55032bcbd83d2d19625db887025/R/eval-select.R для предварительного просмотра того, как будет выглядеть select.default. Мы могли бы даже удалить методы tbl-df и data.frame из dplyr. Последняя строка представляет интерес, она вызывает[
а также names<-
методы.