Inheriting non-list classes in R?

R inheritance can be implemented by extending a list-based class as follows: Suppose lmo

is a class object lm

derived from linear model fitting. The class can be simply extended:

x <- rnorm(1000)
y <- rexp(1000)
lmo <- lm(x~y)

lmo$addition <- "some more information"
class(lmo) <- c("lmext","lm")

      

I could use all the methods like summary.lm

that worked for lm

but also defined custom methods. Obviously there are many situations where you want minimal padding and still want to be able to use all the methods from the parent class.

What is the best way to add additional properties and implement method inheritance for classes that are not list-based like eg. Time series? Here's what I can imagine:

ts1 <- ts(rnorm(100),start = c(1990,1),frequency = 4)
attr(ts1,"additional") <- "some more information"
class(ts1) <- c("tsext","ts")

print.tsext <- 
# some method that uses the original print method for ts, plus extracts
# the additional information

      

Is this a good way to ensure that operators like +

and so on still work without redefining everything for the new class? Is there something better? And is there a way to keep the additional class / attributes, for example adding two series to each other without overriding all the basic operators?

+3


source to share


1 answer


This is an issue with basic functions dropping extra S3 classes:

> foo=1:10
> class(foo)
[1] "integer"
> class(foo)=c("thing","integer")
> class(foo[1:4])
[1] "integer"

      

But how do you Date

get around this?

> dv = as.Date(c("2013-01-01","2013-02-02","2013-02-02","2013-02-06"))
> class(dv)
[1] "Date"
> class(dv[2:3])
[1] "Date"

      

BY overrides [

for class Date

:

> get("[.Date")
function (x, ..., drop = TRUE) 
{
    cl <- oldClass(x)
    class(x) <- NULL
    val <- NextMethod("[")
    class(val) <- cl
    val
}

      

You might have noticed that this method is not mentioned Date

at all in his code - it just gets the old class, calls the default index method, and then reassigns the original class. Quite why this is not the default behavior is a mystery, but it does mean that if you want to create a new vector-based class, you can simply copy this function as your new subset method.

This is the simplest example I know of about the problem of subclassing in R. The rest of this answer will show a few more dangers, and I'll try not to get too much work in the process. I think this is all pertinent to your question.

But unfortunately, non-base classes overuse LOT in R code, and you'll have to write a bunch of other pretty "generic" methods to get your class to work:



> d = data.frame(f=foo,x=1:10)
Error in as.data.frame.default(x[[i]], optional = TRUE) : 
  cannot coerce class ""thing"" to a data.frame

      

now you need to write as.data.frame.thing

which luckily can be the same asas.data.frame.Date

> as.data.frame.thing = as.data.frame.Date
> d = data.frame(f=foo,x=1:10)
> d

      

Great, now you have a class thing

in your dataframe.

Then one day you try to do something with dplyr

using a vector of your class in a dataframe and you get a flattened one:

> d %.% group_by(f) %.% summarise(m=mean(x))
Error in eval(expr, envir, enclos) : column 'f' has unsupported type

      

But dplyr

does it work with objects Date

correctly? This is because deep in C ++ code, it checks for types Date

. At this point, you get discouraged.

These are just a few of the downsides to writing S3 classes that inherit from existing classes. Basically, the stuff doesn't work, at least not the way you might expect if you have experience with OOP in another language.

+5


source







All Articles