如何让自定义R6类NumericInterval作为data.frame列类型正常工作?
Let's break down why you're seeing that error with base data.frame, and walk through two solutions that meet your requirements: displaying the interval string in the table while keeping the R6 objects intact for modification.
Why Base data.frame Throws an Error
Base R's data.frame expects columns to be atomic vectors (like numeric, character) or structures that can be coerced to a data frame. R6 objects are reference-type objects, so as.data.frame.default doesn't know how to handle them—hence the error you ran into.
Solution 1: Make Base data.frame Work with Your R6 Class
We'll create a custom class for lists of NumericInterval objects, then define formatting methods so the data frame displays interval strings while storing the actual R6 objects.
Step 1: Extend Your Existing Code
Keep your original R6 class and S3 methods, then add code to handle list columns:
require(R6) # Your original NumericInterval class NumericInterval <- R6Class( "NumericInterval", public = list( lower_bound = NA, upper_bound = NA, initialize = function(low, up) { self$lower_bound <- low self$upper_bound <- up }, as_character = function() { paste0("[", self$lower_bound, ", ", self$upper_bound, "]") } ) ) # Your existing S3 methods as.character.NumericInterval <- function(x, ...) { x$as_character() } print.NumericInterval <- function(x, ...) { cat(x$as_character(), "\n") } # New: Custom class for lists of NumericInterval objects as.NumericIntervalList <- function(x) { stopifnot(all(sapply(x, inherits, "NumericInterval"))) structure(x, class = "NumericIntervalList") } # New: Format method to display interval strings format.NumericIntervalList <- function(x, ...) { sapply(unclass(x), as.character) }
Step 2: Create the Data Frame
Use I() to prevent base data.frame from trying to coerce the list column, and wrap your R6 objects with our custom list class:
df <- data.frame( X = c("I1", "I2", "I3"), Y = I(as.NumericIntervalList(list( NumericInterval$new(0,1), NumericInterval$new(1,2), NumericInterval$new(2,3) ))), stringsAsFactors = FALSE )
Step 3: Verify Behavior
Now the data frame displays correctly, and you can modify the R6 objects directly:
print(df) # X Y # 1 I1 [0, 1] # 2 I2 [1, 2] # 3 I3 [2, 3] # Modify an object df$Y[[2]]$lower_bound <- 0 # Print again to see the update print(df) # X Y # 1 I1 [0, 1] # 2 I2 [0, 2] # 3 I3 [2, 3] # Access object properties df$Y[[2]]$upper_bound # [1] 2
Solution 2: Optimize Tibble Display for Your R6 Class
Tibbles already support list columns of R6 objects, but we can fix the display using the pillar package to show interval strings instead of <NmrcIntr>.
Step 1: Add a Pillar Formatting Method
Keep your original R6 class and S3 methods, then add a pillar_shaft method for NumericInterval:
require(R6) require(tibble) require(pillar) # Your original NumericInterval class and S3 methods (same as before) NumericInterval <- R6Class( "NumericInterval", public = list( lower_bound = NA, upper_bound = NA, initialize = function(low, up) { self$lower_bound <- low self$upper_bound <- up }, as_character = function() { paste0("[", self$lower_bound, ", ", self$upper_bound, "]") } ) ) as.character.NumericInterval <- function(x, ...) { x$as_character() } print.NumericInterval <- function(x, ...) { cat(x$as_character(), "\n") } # New: Pillar formatting method for tibble display pillar_shaft.NumericInterval <- function(x, ...) { new_pillar_shaft_simple(as.character(x), align = "left") }
Step 2: Create the Tibble
Just pass your R6 objects as a list column:
df <- tibble( X = c("I1", "I2", "I3"), Y = list( NumericInterval$new(0,1), NumericInterval$new(1,2), NumericInterval$new(2,3) ) )
Step 3: Verify Behavior
Now the tibble displays interval strings, and you can modify the R6 objects seamlessly:
print(df) # # A tibble: 3 × 2 # X Y # <chr> <chr> # 1 I1 [0, 1] # 2 I2 [1, 2] # 3 I3 [2, 3] # Modify an object df$Y[[2]]$lower_bound <- 0 # See the update print(df) # # A tibble: 3 × 2 # X Y # <chr> <chr> # 1 I1 [0, 1] # 2 I2 [0, 2] # 3 I3 [2, 3]
Which Solution Should You Choose?
- Use the base
data.framesolution if you prefer working with traditional R data frames and want behavior consistent with base R. - Use the tibble solution if you're part of the tidyverse ecosystem and want a more streamlined setup for list columns.
内容的提问来源于stack exchange,提问作者pietrodito

