Я хочу создать подкласс для data.frame
(и тому подобное). Я хочу функцию
который создает информацию о классе, а другой удаляет ее.
df <- data.frame(x = 1:10)
Думаю, это был бы "санкционированный" способ добавления классов. А также
добавление, а затем удаление класса работает, как ожидалось, возвращая
нетронутый объект.
df2 <- df
class(df2$x) <- c("someclass", class(df2$x))
class(df2$x) <- class(df2$x)[class(df2$x) != "someclass"]
all.equal(df2, df)
#> [1] TRUE
Но этот метод не работает, если я хочу изменить объект по ссылке
потому что class<-
, кажется, делает копию data.table:
add.class <- function(object) {
class(object$x) <- c("someclass", class(object$x))
return(invisible(object))
}
add.class(df)
class(df$x) # doesn't work
#> [1] "integer"
Документация data.table
говорит о правильном способе установки атрибутов.
в качестве ссылки используется setattr
, и, действительно, эта функция работает как
предназначенный
add.class <- function(object) {
data.table::setattr(object$x, "class", c("someclass", class(object$x)))
return(invisible(object))
}
add.class(df)
class(df$x) # works!
#> [1] "someclass" "integer"
Но проблема в том, что при использовании той же логики для удаления класса
объект не возвращается точно в предыдущее состояние
remove.class <- function(object) {
data.table::setattr(object$x, "class", class(object$x)[class(object$x) != "someclass"])
return(invisible(object))
}
df <- data.frame(x = 1:10)
df2 <- data.table::copy(df)
add.class(df2)
remove.class(df2)
all.equal(df, df2) # Not equal!
#> [1] "Component \"x\": Attributes: < target is NULL, current is list >"
#> [2] "Component \"x\": target is numeric, current is integer"
all.equal(class(df$x), class(df2$x)) # But, equal classes??
#> [1] TRUE
Проблема не характерна для data.table::setattr()
, так как обычная
Функция attr<-
имеет ту же проблему при изменении атрибута class
df <- data.frame(x = 1:10)
df2 <- data.table::copy(df)
attr(df$x, "class") <- c("someclass", class(df$x))
attr(df$x, "class") <- class(df$x)[class(df$x) != "someclass"]
all.equal(df, df2)
#> [1] "Component \"x\": Attributes: < Modes: list, NULL >"
#> [2] "Component \"x\": Attributes: < Lengths: 1, 0 >"
#> [3] "Component \"x\": Attributes: < names for target but not for current >"
#> [4] "Component \"x\": Attributes: < current is not list-like >"
#> [5] "Component \"x\": target is integer, current is numeric"
Я предполагаю, что class<-
делает что-то отличное от attr<-
и
setattr()
. Но потом я понял, что это только произойдет, если я изменю
класс столбца. Изменение класса всего объекта работает как
ожидается.
add.class <- function(object) {
data.table::setattr(object, "class", c("someclass", class(object)))
return(invisible(object))
}
remove.class <- function(object) {
data.table::setattr(object, "class", class(object)[class(object) != "someclass"])
return(invisible(object))
}
df <- data.frame(x = 1:10)
df2 <- data.table::copy(df)
add.class(df2)
remove.class(df2)
all.equal(df, df2)
#> [1] TRUE
Это не такая огромная проблема, но это действительно делает боль в заднице
чтобы правильно проверить, так как я не могу использовать testthat::expect_equal()
для устройства
тесты.
Итак, как правильно изменить класс столбца data.frame
ссылка?
Создано в 2018-08-29 пакетом Представление (v0.2.0).