Re: [R] How to make attributes persist after indexing?

From: Marc Schwartz <MSchwartz_at_mn.rr.com>
Date: Thu 25 May 2006 - 12:37:02 EST

On Thu, 2006-05-25 at 00:43 +0100, Heinz Tuechler wrote:
> Thank you for your answer, Gabor. I will see, if I understood it.
>
> Heinz
>
> At 11:31 24.05.2006 -0400, Gabor Grothendieck wrote:
> >You could create your own child class with its own [ method.
> >
> >"[.myfactor" <- function(x, ...) {
> > attr <- attributes(x)
> > x <- NextMethod("[")
> > attributes(x) <- attr
> > x
> >}
> >
> >gx <- structure(fx, class = c("myfactor", class(fx)))
> >attributes(gx[1])
> >

Heinz,

What Gabor has proposed is essentially what Frank Harrell does in the Hmisc package (which I referenced), though in a more generic fashion with respect to the attributes that are saved and reset.

By using:

  gx <- structure(fx, class = c("myfactor", class(fx)))

you are taking an object 'fx' and adding a child class attribute called "myfactor" to it. So it is in effect an object that retains the attributes of the original class of 'fx', plus the new class attribute 'myfactor'.

As a parallel, for example, consider that a square is a child class of a rectangle. A square inherits all of the attributes of a rectangle, plus the additional attribute that all four sides are of equal length. So for example, if 'x' is a square, you might see:

> x

[1] 4 4 4 4
attr(,"class")
[1] "square" "rectangle"

as compared to a rectangle 'y':

> y

[1] 4 2 4 2
attr(,"class")
[1] "rectangle"

Note the two class attributes for 'x', with 'square' preceding 'rectangle' in the order. The order is important, because in R, function methods are dispatched based upon the class attributes in the order that they appear in the vector.

So...back to 'gx'.

When the generic "[" function is called with "gx" (ie. gx[1]), the first class attribute 'myfactor' is picked up from 'gx'. Then, the method that Gabor has presented, "[.myfactor", is dispatched (executed). It is the generic function "[" with the specific class method defined by ".myfactor".

In that function, the first thing that takes place is that the attributes of the 'x' argument are saved in 'attr'.

  attr <- attributes(x)

This would include any new attributes that you have defined and added, such as labels and comments.

The next thing that happens is that the generic "[" is now called again:

  x <- NextMethod("[")

but this time, the method dispatched is based upon the "Next" entry in the class vector. In this case, whatever the original class of 'fx' was, which could be an atomic vector, a factor, a matrix or a data frame, for example.

You can see the methods available for "[" by using:

   methods("[")

The appropriate method for "[" is then executed, resulting in 'x', which is the subset version of 'gx'.

Then:

  attributes(x) <- attr

restores the original attributes saved in 'attr' to 'x'.

Then, finally, the 'x' object is returned to the calling environment.

So, in effect, you have created a new subset function "[" that retains your new attributes. The great thing about this, is that once the method is defined in your working environment, all you have to do is to add the newly associated class attribute to the objects you want to subset and R does the rest transparently.

Thus:

# Add the new method
"[.myfactor" <- function(x, ...) {

       attr <- attributes(x)
       x <- NextMethod("[")
       attributes(x) <- attr
       x

}

# Create fx
fx <- factor(1:5, ordered = TRUE)

attr(fx, 'comment') <- 'Comment for fx'
attr(fx, 'label') <- 'Label for fx'
attr(fx, 'testattribute') <- 'just for fun'

> attributes(fx)

$levels
[1] "1" "2" "3" "4" "5"

$class
[1] "ordered" "factor"

$comment
[1] "Comment for fx"

$label
[1] "Label for fx"

$testattribute
[1] "just for fun"

# Create gx, which is identical to fx, with the # additional class attribute
gx <- structure(fx, class = c("myfactor", class(fx)))

> attributes(gx)

$levels
[1] "1" "2" "3" "4" "5"

$class
[1] "myfactor" "ordered" "factor" # <- Note change here

$comment
[1] "Comment for fx"

$label
[1] "Label for fx"

$testattribute
[1] "just for fun"

# Show the structure of fx[1]
# Note that other attributes are lost
> str(fx[1])

 Ord.factor w/ 5 levels "1"<"2"<"3"<"4"<..: 1

# Show the structure of gx[1]
# Note that attributes are retained
> str(gx[1])

 Ord.factor w/ 5 levels "1"<"2"<"3"<"4"<..: 1

HTH, Marc Schwartz



R-help@stat.math.ethz.ch mailing list
https://stat.ethz.ch/mailman/listinfo/r-help PLEASE do read the posting guide! http://www.R-project.org/posting-guide.html Received on Thu May 25 12:44:58 2006

Archive maintained by Robert King, hosted by the discipline of statistics at the University of Newcastle, Australia.
Archive generated by hypermail 2.1.8, at Thu 25 May 2006 - 20:10:25 EST.

Mailing list information is available at https://stat.ethz.ch/mailman/listinfo/r-help. Please read the posting guide before posting to the list.