Search code examples
rinheritancer-s4

R: Applying terms.formula on an S4 object inheriting data.frame


I'm trying to create a new class that inherits from data frame:

> setClass('new.frame',
    representation(colour='character'),
    contains = 'data.frame')

This is an instance of that class, for testing:

> test_data = data.frame(cbind(runif(5), runif(5)))
> names(test_data) = c('X', 'Y')
> test_frame = new('new.frame', test_data, colour='red')

Just to make sure it looks all right...

> data.frame
Object of class "new.frame"
          X         Y
1 0.8766306 0.4741213
2 0.1221508 0.5117665
3 0.4838761 0.4973627
4 0.7858294 0.4064749
5 0.5147703 0.9135304
Slot "colour":
[1] "red"

... and to make sure that the inheritance worked

> is.data.frame(test_frame)
[1] TRUE
> getClass(class(test_frame))
Class "new.frame" [in ".GlobalEnv"]

Slots:

Name:                .Data              colour               names
Class:                list           character           character

Name:            row.names            .S3Class
Class: data.frameRowLabels           character

Extends: 
Class "data.frame", directly
Class "list", by class "data.frame", distance 2
Class "oldClass", by class "data.frame", distance 2
Class "vector", by class "data.frame", distance 3

Here is the problem I encountered when I tried to utilize the property of being a data frame:

> terms.formula(Y ~ X, data = test_frame)
Error in terms.formula(Y ~ X, data = test_frame) : 
  'data' argument is of the wrong type

I might have missed something silly. If so, thanks in advance for pointing it out.

If I'm correct about the problem here, is there anyway I can make terms.formula recognize the fact that I'm giving it a data.frame?


Solution

  • Doing debug(terms.formula) and then running terms.formula(Y ~ X, data = test_frame) shows that your code is failing on lines 3 and 4 of the quoted code block:

    if (!is.null(data) && !is.environment(data) && !is.data.frame(data)) 
        data <- as.data.frame(data, optional = TRUE)
    terms <- .Internal(terms.formula(x, specials, data, keep.order, 
        allowDotAsName))
    

    The problem must be that the call to .Internal(terms.formula()) expects a 'plain old' data.frame, and is instead being passed an object of class new.frame. As a workaround, why not just pass terms.formula() the type of object it expects (an unadorned data.frame)?

    Here's one easy way to do that:

    terms.formula(Y ~ X, data = unclass(test_frame))
    # Y ~ X
    # attr(,"variables")
    # list(Y, X)
    # attr(,"factors")
    #   X
    # Y 0
    # X 1
    # attr(,"term.labels")
    # [1] "X"
    # attr(,"order")
    # [1] 1
    # attr(,"intercept")
    # [1] 1
    # attr(,"response")
    # [1] 1
    # attr(,".Environment")
    # <environment: R_GlobalEnv>