Как я могу зафиксировать изменения, внесенные в 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<- методы.

Другие вопросы по тегам