pie*_*ito 4 types r dataframe r6 tibble
我想有几种方法可以做到这一点。因此,这个问题的答案即使不是主观的,也可能是主观的。因此,我将尝试缩小问题范围,并向您提供我已经完成的操作的详细信息。
\n\n我正在使用该R6包,并且创建了一个IntervalNumeric\nR6Class,它有两个字段lower_bound和upper_bound:
require(R6)\nNumericInterval <-\n R6Class(\n "NumericInterval",\n public = list(\n lower_bound = NA,\n upper_bound = NA,\n initialize = function(low, up) {\n self$lower_bound <- low\n self$upper_bound <- up\n },\n as_character = function() {\n paste0("[", self$lower_bound, ", ",\n self$upper_bound, "]")}))\nRun Code Online (Sandbox Code Playgroud)\n\n我还使用S3通用方法系统来获取\n类型的as.characterand :printNumericInterval
as.character.NumericInterval <- function(x, ...) {\n x$as_character()}\nprint.NumericInterval <- function(x, ...) {\n x$as_character()}\nRun Code Online (Sandbox Code Playgroud)\n\n现在我可以这样做(与 相同print):
> as.character(NumericInterval$new(0, pi))\n\n[1] "[0, 3.14159265358979]"\nRun Code Online (Sandbox Code Playgroud)\n\n现在需要做什么才能使用这个新类型作为data.frame列类型?
例如我希望能够做到这一点:
\n\n(df <- data.frame(\n X = c("I1", "I2", "I3"),\n Y = c(NumericInterval$new(0,1),\n NumericInterval$new(1,2),\n NumericInterval$new(2,3)))\nRun Code Online (Sandbox Code Playgroud)\n\n并得到:
\n\n X Y\n1 I1 [0, 1]\n2 I2 [1, 2]\n3 I3 [2, 3]\nRun Code Online (Sandbox Code Playgroud)\n\n但我得到:
\n\nError in as.data.frame.default(x[[i]], optional = TRUE) :\n cannot coerce class \xe2\x80\x98c("NumericInterval", "R6")\xe2\x80\x99 to a data.frame\nRun Code Online (Sandbox Code Playgroud)\n\n当然,我还希望能够访问对象并执行以下操作:
\n\ndf[2, 2]$lower_bound <- 0\nRun Code Online (Sandbox Code Playgroud)\n\ntibbles似乎是一个解决方案(df <- tibble(\nX = c("I1", "I2", "I3"),\nY = c(NumericInterval$new(0,1),\nNumericInterval$new(1,2),\nNumericInterval$new(2,3))))\nRun Code Online (Sandbox Code Playgroud)\n\n产生:
\n\n# A tibble: 3 x 2\n X Y\n <chr> <list>\n1 I1 <NmrcIntr>\n2 I2 <NmrcIntr>\n3 I3 <NmrcIntr>\nRun Code Online (Sandbox Code Playgroud)\n\n每个都NumericInterval按预期放置,例如:
> require(dplyr)\n> df[2,1][[1]] %>% pull\n\n\n[[1]]\n<NumericInterval>\n Public:\n as_character: function ()\n clone: function (deep = FALSE)\n initialize: function (low, up)\n lower_bound: 0\n upper_bound: 1\nRun Code Online (Sandbox Code Playgroud)\n\n但 tibble 的输出和访问对象的方式并不是我所期望的。
\n在将 R6 对象强制到数据帧中之前,您需要做出一些设计决策。也许最重要的是您希望矢量化发生在什么级别。
在您的示例中,您将“原子”NumericInterval放入向量中,这当然有一些优点,但主要缺点是,当您尝试在集合上使用基本 R 向量函数时NumericInterval,R 会将对象视为环境(这就是 R6 对象)。这意味着您不会获得您正在寻找的行为,因为您希望 R 处理这些环境向量的方式与通常处理环境向量的方式不同。换句话说,要处理这种工作方式,您需要定义另一个类,其中包含管理向量操作的方法。这是可能的,但看起来复杂、混乱且效率低下。
在我看来,最好将向量化保留在单个 R6 对象内 - 也就是说,在单个 R6 对象内包含lower_bounds和 的向量。upper_boundsR6 类可以处理打印、格式化和子集设置,并且可以充当数据帧本身中的整个列。
为此,您首先需要定义一些泛型函数的 R6 特化:
`[.R6` <- function(x, ...) x$`[`(...)
`[<-.R6` <- function(x, ...) x$`[<-`(...)
length.R6 <- function(x) x$length()
format.R6 <- function(x) x$format()
as.data.frame.R6 <- function(x, ...) x$as.data.frame()
Run Code Online (Sandbox Code Playgroud)
将它们作为.R6而不是NumericInterval允许您在多个不同的类中使用它们。
现在我们可以用我们需要的专业来定义我们的类:
NumericInterval <- R6Class("NumericInterval",
public = list(
lower_bound = NA,
upper_bound = NA,
initialize = function(low, up) {
self$lower_bound <- low
self$upper_bound <- up
},
`[` = function(n){
return(NumericInterval$new(self$lower_bound[n], self$upper_bound[n]))
},
`[<-` = function(n, m){
self$lower_bound[n] <- m[1]
self$upper_bound[n] <- m[2]
invisible(self)
},
length = function() length(self$lower_bound),
as.data.frame = function(...) {
structure(
list(interval = structure(a)),
class = "data.frame",
row.names = seq_along(self$lower_bound))
},
as_character = function() {
paste0("[", self$lower_bound, ", ",
self$upper_bound, "]")},
format = function(...) self$as_character(),
print = function() {
print(self$as_character(), quote = FALSE)
invisible(self)}))
Run Code Online (Sandbox Code Playgroud)
这会产生以下行为:
a <- NumericInterval$new(1:3, 4:6)
a
#> [1] [1, 4] [2, 5] [3, 6]
as.data.frame(a)
#> interval
#> 1 [1, 4]
#> 2 [2, 5]
#> 3 [3, 6]
df <- data.frame(id = LETTERS[1:3], interval = a)
df
#> id interval
#> 1 A [1, 4]
#> 2 B [2, 5]
#> 3 C [3, 6]
df[1,]
#> id interval
#> 1 A [1, 4]
df$interval[1]$lower_bound
#> [1] 1
Run Code Online (Sandbox Code Playgroud)
这当然不是生产级代码。特别是,您需要包括错误处理以确保上限和下限具有相同的长度,并且都是数字。