[Rd] Strange R object
Deepayan Sarkar
deepayan.sarkar at gmail.com
Fri Jul 9 18:41:53 CEST 2010
On Fri, Jul 9, 2010 at 5:25 AM, Peter Dalgaard <pdalgd at gmail.com> wrote:
> Gabor Grothendieck wrote:
>> On Fri, Jul 9, 2010 at 5:09 AM, Peter Dalgaard <pdalgd at gmail.com> wrote:
>>> Gabor Grothendieck wrote:
>>>> I have *** attached *** an RData file containing an R object that
>>>> is acting strangely.
>>>>
>>>> Try this in a fresh workspace. Do not load zoo or any other package.
>>>> We load the object, zz2, from the attached RData file. It is just
>>>> the number 1 with the class c("zooreg", "zoo").
>>>>
>>>> Now create an S3 print routine that simply prints an X when given
>>>> an object of class "zoo".
>>>>
>>>> If we use print on the object it produces an X but not if we just
>>>> enter it at the console. Also the object is not identical to its
>>>> dput output.
>>>>
>>>> How can such an object exist? What is it about the object that is
>>>> different from structure(1, class = c("zoo", "zooreg")) ?
>>>>
>>> There's a bit in the SEXP structure that is supposed to be turned on
>>> when an object has an S3 class. This is where implicit print looks,
>>> whereas explicit print looks, er, elsewhere. Notice that
>>>
>>>> is.object(zz2)
>>> [1] FALSE
>>>> class(zz2) <- class(zz2)
>>>> zz2
>>> X
>>>> is.object(zz2)
>>> [1] TRUE
>>>
>>> Whenever the same information is stored in two ways, there is a risk of
>>> inconsistency, so it is not too strange that you can have an ill-formed
>>> .Rdata file (if you save zz2 back out, after the above fixup, line 11
>>> changes from 526 to 782, corresponding to the bit being turned on).
>>>
>>> I don't think it is the job of load() to verify object structures, since
>>> there is no end to that task. Rather, we shouldn't create them in the
>>> first place, but you give us no clues as to how that object got made.
>>>
>>
>> This was originally a large object in a program that uses a variety of
>> packages and it took quite a long time just to narrow it down to the
>> point where I had an object sufficiently small to post. Its not even
>> clear at what point the object goes bad but your class(x) <- class(x)
>> trick helped a lot and I have now been able to recreate it in a simple
>> manner.
>>
>> Below we create a new S3 class "X" with an Ops.X and print.X method.
>> We then create an object x of that class which is just 1 with a class
>> of "X". When we multiply 1*x we get the bad object. 1*x and x have
>> the same dput output but compare as FALSE. 1*x is not printed by
>> print.X even though it is of class "X" while x is printed by print.X .
>> If we assign 1*x to xx and use your class assignment trick (class(xx)
>> <- class(xx)) then xx prints as expected even though it did not prior
>> to the class assignment.
>>
>>> Ops.X <- function(e1, e2) { print("Ops.X"); NextMethod(.Generic) }
>>> print.X <- function(x, ...) print("print.X")
>>> x <- structure(1, class = "X")
>>> dput(x)
>> structure(1, class = "X")
>>> dput(1*x)
>> [1] "Ops.X"
>> structure(1, class = "X")
>>> identical(x, 1*x)
>> [1] "Ops.X"
>> [1] FALSE
>>> 1*x
>> [1] "Ops.X"
>> [1] 1
>> attr(,"class")
>> [1] "X"
>>> x
>> [1] "print.X"
>>> xx <- 1*x
>> [1] "Ops.X"
>>> class(xx) <- class(xx)
>>> xx
>> [1] "print.X"
>
> Or, to minimize it further:
>
>> x <- structure(1, class="y")
>> is.object(x)
> [1] TRUE
>> is.object(x*1)
> [1] TRUE
>> is.object(1*x)
> [1] FALSE
>> class(x*1)
> [1] "y"
>> class(1*x)
> [1] "y"
>
> Yup, that looks like a bug.
I recently came across the following surprising behaviour which turns
out to be the same issue. I had been meaning to ask for an
explanation.
> x <- 1:20
> class(x)
[1] "integer"
> is.object(x)
[1] FALSE
> print.integer <- function(x) print(x %% 5)
> print(x)
[1] 1 2 3 4 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4 0
> x
[1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
-Deepayan
More information about the R-devel
mailing list