qed
qed

Reputation: 23154

Can an object in R have more than one class?

Suppose I have a function like this:

myf = function(x) {
res = dostuff(x)
res # this is a data.frame
}

I want to do something special about res, for example, I want to make some generic functions like print.myf, summary.myf, ... , so I can go ahead and give it a class:

myf = function(x) {
res = dostuff(x)
class(res) = 'myf'
res
}

But this way I cannot use it as a data.frame any more.

Upvotes: 5

Views: 918

Answers (2)

A5C1D2H2I1M1N2O1R2T1
A5C1D2H2I1M1N2O1R2T1

Reputation: 193687

Here's an example with rle. The rle function creates a list, but it doesn't have the class list, so methods like as.data.frame won't work "out of the box". As such, I've changed the last line to add "list" as one of the classes.

x <- c(1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3)

rle2 <- function (x)
{
  if (!is.vector(x) && !is.list(x))
    stop("'x' must be an atomic vector")
  n <- length(x)
  if (n == 0L)
    return(structure(list(lengths = integer(), values = x),
                     class = "rle"))
  y <- x[-1L] != x[-n]
  i <- c(which(y | is.na(y)), n)

  ## THE FOLLOWING IS FROM THE BASE RLE. NOTICE ONLY ONE CLASS...
  # structure(list(lengths = diff(c(0L, i)), values = x[i]), 
  #  class = "rle")

  structure(list(lengths = diff(c(0L, i)), values = x[i]),
            class = c("rle", "list"))
}

As you can see, I've simply changed the class. The rest of the function is the same.

rle(x)
# Run Length Encoding
#   lengths: int [1:3] 4 3 7
#   values : num [1:3] 1 2 3
data.frame(rle(x))
# Error in as.data.frame.default(x[[i]], optional = TRUE, stringsAsFactors = stringsAsFactors) : 
#   cannot coerce class ""rle"" to a data.frame
rle2(x)
# Run Length Encoding
#   lengths: int [1:3] 4 3 7
#   values : num [1:3] 1 2 3
data.frame(rle2(x))
#   lengths values
# 1       4      1
# 2       3      2
# 3       7      3

Of course, if we know this, we could also explicitly specify our methods if we know one exists:

as.data.frame.list(rle(x))
#   lengths values
# 1       4      1
# 2       3      2
# 3       7      3

Upvotes: 3

Dirk is no longer here
Dirk is no longer here

Reputation: 368629

Yes, my standard (simple) example is

R> now <- Sys.time()
R> class(now)
[1] "POSIXct" "POSIXt" 
R> class(as.POSIXlt(now))
[1] "POSIXlt" "POSIXt" 
R> 

It is also the reason behind the pro tip of testing with inherits("someClass") rather than testing the result of class(obj)=="someClass".

Upvotes: 4

Related Questions