Davis Vaughan
2023-Apr-28 15:12 UTC
[Rd] range() for Date and POSIXct could respect `finite = TRUE`
Hi all, I noticed that `range.default()` has a nice `finite = TRUE` argument, but it doesn't actually apply to Date or POSIXct due to how `is.numeric()` works. ``` x <- .Date(c(0, Inf, 1, 2, Inf)) x #> [1] "1970-01-01" "Inf" "1970-01-02" "1970-01-03" "Inf" # Darn! range(x, finite = TRUE) #> [1] "1970-01-01" "Inf" # What I want .Date(range(unclass(x), finite = TRUE)) #> [1] "1970-01-01" "1970-01-03" ``` I think `finite = TRUE` would be pretty nice for Dates in particular. As a motivating example, sometimes you have ranges of dates represented by start/end pairs. It is fairly natural to represent an event that hasn't ended yet with an infinite date. If you need to then compute a sequence of dates spanning the full range of the start/end pairs, it would be nice to be able to use `range(finite = TRUE)` to do so: ``` start <- as.Date(c("2019-01-05", "2019-01-10", "2019-01-11", "2019-01-14")) end <- as.Date(c("2019-01-07", NA, "2019-01-14", NA)) end[is.na(end)] <- Inf # `end = Inf` means that the event hasn't "ended" yet data.frame(start, end) #> start end #> 1 2019-01-05 2019-01-07 #> 2 2019-01-10 Inf #> 3 2019-01-11 2019-01-14 #> 4 2019-01-14 Inf # Create a full sequence along all days in start/end range <- .Date(range(unclass(c(start, end)), finite = TRUE)) seq(range[1], range[2], by = 1) #> [1] "2019-01-05" "2019-01-06" "2019-01-07" "2019-01-08" "2019-01-09" #> [6] "2019-01-10" "2019-01-11" "2019-01-12" "2019-01-13" "2019-01-14" ``` It seems like one option is to create a `range.Date()` method that unclasses, forwards the arguments on to a second call to `range()`, and then reclasses? ``` range.Date <- function(x, ..., na.rm = FALSE, finite = FALSE) { .Date(range(unclass(x), na.rm = na.rm, finite = finite), oldClass(x)) } ``` This is similar to how `rep.Date()` works. Thanks, Davis Vaughan
Paul McQuesten
2023-Apr-28 15:29 UTC
[Rd] range() for Date and POSIXct could respect `finite = TRUE`
A tiny nit-pick: Seems to me that end date = NA would mean the event has not yet ended, whilst Inf would mean that the event is known to never terminate, ie: an eternal fact, or physical law. On Fri, Apr 28, 2023 at 10:12?AM Davis Vaughan via R-devel < r-devel at r-project.org> wrote:> Hi all, > > I noticed that `range.default()` has a nice `finite = TRUE` argument, > but it doesn't actually apply to Date or POSIXct due to how > `is.numeric()` works. > > ``` > x <- .Date(c(0, Inf, 1, 2, Inf)) > x > #> [1] "1970-01-01" "Inf" "1970-01-02" "1970-01-03" "Inf" > > # Darn! > range(x, finite = TRUE) > #> [1] "1970-01-01" "Inf" > > # What I want > .Date(range(unclass(x), finite = TRUE)) > #> [1] "1970-01-01" "1970-01-03" > ``` > > I think `finite = TRUE` would be pretty nice for Dates in particular. > > As a motivating example, sometimes you have ranges of dates > represented by start/end pairs. It is fairly natural to represent an > event that hasn't ended yet with an infinite date. If you need to then > compute a sequence of dates spanning the full range of the start/end > pairs, it would be nice to be able to use `range(finite = TRUE)` to do > so: > > ``` > start <- as.Date(c("2019-01-05", "2019-01-10", "2019-01-11", "2019-01-14")) > end <- as.Date(c("2019-01-07", NA, "2019-01-14", NA)) > end[is.na(end)] <- Inf > > # `end = Inf` means that the event hasn't "ended" yet > data.frame(start, end) > #> start end > #> 1 2019-01-05 2019-01-07 > #> 2 2019-01-10 Inf > #> 3 2019-01-11 2019-01-14 > #> 4 2019-01-14 Inf > > # Create a full sequence along all days in start/end > range <- .Date(range(unclass(c(start, end)), finite = TRUE)) > seq(range[1], range[2], by = 1) > #> [1] "2019-01-05" "2019-01-06" "2019-01-07" "2019-01-08" "2019-01-09" > #> [6] "2019-01-10" "2019-01-11" "2019-01-12" "2019-01-13" "2019-01-14" > ``` > > It seems like one option is to create a `range.Date()` method that > unclasses, forwards the arguments on to a second call to `range()`, > and then reclasses? > > ``` > range.Date <- function(x, ..., na.rm = FALSE, finite = FALSE) { > .Date(range(unclass(x), na.rm = na.rm, finite = finite), oldClass(x)) > } > ``` > > This is similar to how `rep.Date()` works. > > Thanks, > Davis Vaughan > > ______________________________________________ > R-devel at r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel >[[alternative HTML version deleted]]
Martin Maechler
2023-Apr-29 20:47 UTC
[Rd] range() for Date and POSIXct could respect `finite = TRUE`
>>>>> Davis Vaughan via R-devel >>>>> on Fri, 28 Apr 2023 11:12:27 -0400 writes:> Hi all, > I noticed that `range.default()` has a nice `finite > TRUE` argument, but it doesn't actually apply to Date or > POSIXct due to how `is.numeric()` works. Well, I think it would / should never apply: range() belongs to the "Summary" group generics (as min, max, ...) and there *are* Summary.Date() and Summary.POSIX{c,l}t() methods. Without checking further for now, I think you are indirectly suggesting to enhance these three Summary.*() methods so they do obey 'finite = TRUE' . I think I agree they should. Martin > ``` x <- .Date(c(0, Inf, 1, 2, Inf)) x #> [1] "1970-01-01" > "Inf" "1970-01-02" "1970-01-03" "Inf" > # Darn! range(x, finite = TRUE) #> [1] "1970-01-01" "Inf" > # What I want .Date(range(unclass(x), finite = TRUE)) #> > [1] "1970-01-01" "1970-01-03" ``` > I think `finite = TRUE` would be pretty nice for Dates in > particular. > As a motivating example, sometimes you have ranges of > dates represented by start/end pairs. It is fairly natural > to represent an event that hasn't ended yet with an > infinite date. If you need to then compute a sequence of > dates spanning the full range of the start/end pairs, it > would be nice to be able to use `range(finite = TRUE)` to > do so: > ``` start <- as.Date(c("2019-01-05", "2019-01-10", > "2019-01-11", "2019-01-14")) end <- > as.Date(c("2019-01-07", NA, "2019-01-14", NA)) > end[is.na(end)] <- Inf > # `end = Inf` means that the event hasn't "ended" yet > data.frame(start, end) #> start end #> 1 2019-01-05 > 2019-01-07 #> 2 2019-01-10 Inf #> 3 2019-01-11 2019-01-14 > #> 4 2019-01-14 Inf > # Create a full sequence along all days in start/end range > <- .Date(range(unclass(c(start, end)), finite = TRUE)) > seq(range[1], range[2], by = 1) #> [1] "2019-01-05" > "2019-01-06" "2019-01-07" "2019-01-08" "2019-01-09" #> [6] > "2019-01-10" "2019-01-11" "2019-01-12" "2019-01-13" > "2019-01-14" ``` > It seems like one option is to create a `range.Date()` > method that unclasses, forwards the arguments on to a > second call to `range()`, and then reclasses? > ``` range.Date <- function(x, ..., na.rm = FALSE, finite > FALSE) { .Date(range(unclass(x), na.rm = na.rm, finite > finite), oldClass(x)) } ``` > This is similar to how `rep.Date()` works. > Thanks, Davis Vaughan > ______________________________________________ > R-devel at r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel