From ripley at stats.ox.ac.uk  Mon Jun 26 15:19:13 2006
From: ripley at stats.ox.ac.uk (Prof Brian Ripley)
Date: Mon, 26 Jun 2006 14:19:13 +0100 (BST)
Subject: [Rd] Ops.Date: promote characters to Dates?
In-Reply-To: <449EF2F5.3010902@ufl.edu>
References: <loom.20060624T165439-818@post.gmane.org>
	<971536df0606240823l14bfbd4mc1db38af7d23685f@mail.gmail.com>
	<449EF2F5.3010902@ufl.edu>
Message-ID: <Pine.LNX.4.64.0606261018380.21453@gannet.stats.ox.ac.uk>

On Sun, 25 Jun 2006, Ben Bolker wrote:

>    Believe it or not, it works either way: I haven't fully
> figured out the logic yet (except to note that I had the
> logic reversed below):

This is as documented (White Book p.473, referenced on the help page for 
'Ops').  [Surely part of the homework that the posting guide asks for.]

I have recently been expanding the help pages for group generics (and 
trying to remove some of the confusion caused by adding S4 group generics 
to the page for S3 group generics).  This now says (about S3 group 
generics only)

         2.  Group '"Ops"':

            *  '"+"', '"-"', '"*"', '"/"', '"^"', '"%%"', '"%/%"'

            *  '"&"', '"|"', '"!"'

            *  '"=="', '"!="', '"<"', '"<="', '">="', '">"'

         The classes of both arguments are considered in dispatching.
         If a method is found for just one or the same method is found
         for both, it is used.  If different methods are found, there is
         a warning about 'incompatible methods': in that case or if no
         method is found for either argument the default method is used.

> Ops.Date  <- function (e1, e2)
>  {
>    if (nargs() == 1)
>         stop("unary ", .Generic, " not defined for Date objects")
>     boolean <- switch(.Generic, "<" = , ">" = , "==" = , "!=" = ,
>         "<=" = , ">=" = TRUE, FALSE)
>     if (!boolean)
>         stop(.Generic, " not defined for Date objects")
>     if (nchar(e1)) e1 <- as.Date(e1)
>     if (nchar(e2)) e2 <- as.Date(e2)
>     NextMethod(.Generic)
> }

Eh?  nchar is non-zero for almost all objects to be passed through here, 
including any coercible to Date.  (Remember nchar will itself coerce to 
character.)  And nchar(e1) will be a vector, not a scalar: perhaps 
is.character(e1) was what was intended?


I believe the issue is more general: naive users (those who confuse an 
object with its printed representation) expect to be able to compare 
classed objects to character strings, and for the classed objects to be 
converted by as.character() when doing so.  That is not what is done: 
coerceVector(x, STRSXP) is used internally.  At some point we need to 
consider changing that, but it is not easy as method dispatch depends on 
the environment, something that is often not known when coerceVector is 
invoked.


> datechar1 = "1999-12-03"
> datechar2 = "2000-12-07"
> date1 = as.Date(datechar1)
> date2 = as.Date(datechar2)
>
> date1 == datechar1
> datechar1 == date1
>
> datechar1 < date2
> date2 > datechar1
>
>    I also propose
>
> as.Date.numeric <- function(x, ...) {
>    class(x) <- "Date"
>    x
> }
>
>   this takes a number of seconds since 1970 and
> converts it into a Date object ...

That it would not do so is further evidence of the good reasons for not 
providing such a method for people to misinterpret.  (It would be the 
number of days since 1970-01-01, but probably one would want to take 
floor() of a number here.)

>    cheers
>      Ben
>
>
> Gabor Grothendieck wrote:
>> Note that the first argument still cannot be character
>> since Ops.Date won't get dispatched in that case.
>>
>> On 6/24/06, Ben Bolker <bolker at ufl.edu> wrote:
>>>  Ops.Date  <- function (e1, e2)
>>> {
>>>    if (nargs() == 1)
>>>        stop("unary ", .Generic, " not defined for Date objects")
>>>    boolean <- switch(.Generic, "<" = , ">" = , "==" = , "!=" = ,
>>>        "<=" = , ">=" = TRUE, FALSE)
>>>    if (!boolean)
>>>        stop(.Generic, " not defined for Date objects")
>>> +    if (!nchar(e1)) e1 <- as.Date(e1)
>>> +    if (!nchar(e2)) e2 <- as.Date(e2)
>>>    NextMethod(.Generic)
>>> }
>>>
>>>  adding the above two lines to Ops.Date makes, e.g.
>>>
>>> as.Date("1999-12-13") == "1999-06-14"
>>>
>>> behave as "expected".

Depends who is doing the expecting ....

>>>  Does it seem like a good idea?
>>>  (I was inspired by a student's confusion, and by
>>> the fact that Ops.factor does a similar thing -- although
>>> in this case it seems more sensible to promote the
>>> character to a Date rather than demoting the Date
>>> to a character (in which case comparisons might not
>>> work right?))
>>>
>>>  Similar questions might apply to Ops.POSIXt ...
>>>
>>>  Ben Bolker
>>>
>>> ______________________________________________
>>> R-devel at r-project.org mailing list
>>> https://stat.ethz.ch/mailman/listinfo/r-devel
>>>
>
> ______________________________________________
> R-devel at r-project.org mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel
>
>

-- 
Brian D. Ripley,                  ripley at stats.ox.ac.uk
Professor of Applied Statistics,  http://www.stats.ox.ac.uk/~ripley/
University of Oxford,             Tel:  +44 1865 272861 (self)
1 South Parks Road,                     +44 1865 272866 (PA)
Oxford OX1 3TG, UK                Fax:  +44 1865 272595



[Rd] Ops.Date: promote characters to Dates?
Prof Brian Ripley