Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@

7. `fread`'s `fill` argument now also accepts an `integer` in addition to boolean values. `fread` always guesses the number of columns based on reading a sample of rows in the file. When `fill=TRUE`, `fread` stops reading and ignores subsequent rows when this estimate winds up too low, e.g. when the sampled rows happen to exclude some rows that are even wider, [#2727](https://github.com/Rdatatable/data.table/issues/2727) [#2691](https://github.com/Rdatatable/data.table/issues/2691) [#4130](https://github.com/Rdatatable/data.table/issues/4130) [#3436](https://github.com/Rdatatable/data.table/issues/3436). Providing an `integer` as argument for `fill` allows for a manual estimate of the number of columns instead, [#1812](https://github.com/Rdatatable/data.table/issues/1812) [#5378](https://github.com/Rdatatable/data.table/issues/5378). Thanks to @jangorecki, @christellacaze, @Yiguan, @alexdthomas, @ibombonato, @Befrancesco, @TobiasGold for reporting/requesting, and Benjamin Schwendinger for the PR.

8. `round()` method for `IDate` gains an argument `week_start` to specify to which weekday to round each input date, e.g. `week_start=2L` means "round to the nearest Monday". Note that due to peculiarities of the `round()` generic, this feature is not available for R<4.4.0. Further, to avoid such a recent R version dependency, this argument is not listed in the signature of `round.IDate`, instead being picked up from `...`.

## BUG FIXES

1. `unique()` returns a copy the case when `nrows(x) <= 1` instead of a mutable alias, [#5932](https://github.com/Rdatatable/data.table/pull/5932). This is consistent with existing `unique()` behavior when the input has no duplicates but more than one row. Thanks to @brookslogan for the report and @dshemetov for the fix.
Expand All @@ -38,7 +40,7 @@

5. `fwrite(x, row.names=TRUE)` with `x` a `matrix` writes `row.names` when present, not row numbers, [#5315](https://github.com/Rdatatable/data.table/issues/5315). Thanks to @Liripo for the report, and @ben-schwen for the fix.

6. `round()` method for `IDate` has more natural behavior for `"weeks"` -- in particular, the first week of the year will always have 7 days instead of 6, [#4334](https://github.com/Rdatatable/data.table/pull/4334). Thanks @artemklevtsov for the report and fix.
6. `round()` method for `IDate` has more natural behavior for `"weeks"` -- in particular, the first week of the year will always have 7 days instead of 6, [#4335](https://github.com/Rdatatable/data.table/issues/4335). Thanks @artemklevtsov for the report and fix.

## NOTES

Expand Down
23 changes: 22 additions & 1 deletion R/IDateTime.R
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,35 @@ min.IDate = max.IDate = function(x, ...) {
# fix for #1315
as.list.IDate = function(x, ...) NextMethod()

# nocov start: not tested on current R release (4.3.3); TODO(R>=4.4.0): revisit.
round_weeks = function(x, week_start) {
if (missing(week_start) || week_start == "Jan 1") return(round(x, "year") + 7L * ((yday(x) - 1L) %/% 7L))
# derivation: week_start=k === k+2 mod 7 since Jan 1, 1970 is a Thursday (wday=5).
# find the mapping of {0,1,2,3,4,5,6} to {-3,-2,-1,0,1,2,3}, which is always a right+down shift of x |-> x mod 7.
if (is.numeric(week_start)) return(x + (3L - ((as.integer(x) - (as.integer(week_start) - 1L)) %% 7L)))
week_start = switch(week_start,
Sun = , Sunday = 1L,
Mon = , Monday = 2L,
Tue = , Tuesday = 3L,
Wed = , Wednesday = 4L,
Thu = , Thursday = 5L,
Fri = , Friday = 6L,
Sat = , Saturday = 7L,
stopf("Unrecognized value of 'week_start' %s; expected 'Jan 1', a numeric day of the week, or a weekday name (in English).", week_start)
)
round_weeks(x, week_start)
}
# nocov end

# rounding -- good for graphing / subsetting
## round.IDate = function (x, digits, units=digits, ...) {
## if (missing(digits)) digits = units # workaround to provide a units argument to match the round generic and round.POSIXt
## units = match.arg(digits, c("weeks", "months", "quarters", "years"))
# TODO(R>=4.4.0): Put week_start directly into the method signature.
round.IDate = function (x, digits=c("weeks", "months", "quarters", "years"), ...) {
units = match.arg(digits)
as.IDate(switch(units,
weeks = round(x, "year") + 7L * ((yday(x) - 1L) %/% 7L),
weeks = round_weeks(x, ...),
months = ISOdate(year(x), month(x), 1L),
quarters = ISOdate(year(x), 3L * (quarter(x)-1L) + 1L, 1L),
years = ISOdate(year(x), 1L, 1L)))
Expand Down
12 changes: 12 additions & 0 deletions inst/tests/tests.Rraw
Original file line number Diff line number Diff line change
Expand Up @@ -18425,3 +18425,15 @@ dt = data.table(a = 1L)
test(2252.1, dt[, b:=2L], error = "\\[ was called on a data.table.*not data.table-aware.*':='")
test(2252.2, dt[, let(b=2L)], error = "\\[ was called on a data.table.*not data.table-aware.*'let'")
rm(.datatable.aware)

# week_start parameter for round.IDate
# NB: this is only possible once the round generic accepts >1 argument, in devel but not any release as of this writing (4.3.3)
if ("..." %in% names(formals(.GenericArgsEnv$round))) {
x = as.IDate("2024-03-26") + 0:6
test(2253.1, round(x, "weeks", week_start=1L), ans<-rep(as.IDate(c("2024-03-24", "2024-03-31")), c(2L, 5L)))
test(2253.2, round(x, "weeks", week_start="Sun"), ans)
test(2253.3, round(x, "weeks", week_start="Sunday"), ans)
test(2253.4, round(x, "weeks", week_start=3L), rep(as.IDate(c("2024-03-26", "2024-04-02")), c(4L, 3L)))
test(2253.5, round(x, "weeks", week_start=7L), rep(as.IDate(c("2024-03-23", "2024-03-30")), c(1L, 6L)))
test(2253.6, round(x, "weeks", week_start="Sonntag"), error="Unrecognized value of 'week_start' Sonntag")
}
22 changes: 16 additions & 6 deletions man/IDateTime.Rd
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ as.IDate(x, \dots)
\method{as.IDate}{Date}(x, \dots)
\method{as.Date}{IDate}(x, \dots)
\method{as.POSIXct}{IDate}(x, tz = "UTC", time = 0, \dots)
\method{round}{IDate}(x, digits = c("weeks", "months", "quarters","years"), \ldots)
\method{round}{IDate}(x, digits = c("weeks", "months", "quarters", "years"), \ldots)

as.ITime(x, \dots)
\method{as.ITime}{default}(x, \dots)
Expand Down Expand Up @@ -102,9 +102,10 @@ yearqtr(x)

\arguments{
\item{x}{an object}
\item{\dots}{arguments to be passed to or from other methods. For
\code{as.IDate.default}, arguments are passed to \code{as.Date}. For
\code{as.ITime.default}, arguments are passed to \code{as.POSIXlt}.}
\item{\dots}{arguments to be passed to or from other methods.
For \code{as.IDate.default}, arguments are passed to \code{as.Date}.
For \code{as.ITime.default}, arguments are passed to \code{as.POSIXlt}.
See Details for the special case of \code{round.IDate}.}
\item{tz}{time zone (see \code{strptime}).}
\item{date}{date object convertible with \code{as.IDate}.}
\item{time}{time-of-day object convertible with \code{as.ITime}.}
Expand Down Expand Up @@ -169,8 +170,17 @@ be used, but these return character values, so they must be converted to
factors for use with data.table. \code{isoweek} is ISO 8601-consistent.

The \code{round} method for IDate's is useful for grouping and plotting.
It can round to weeks, months, quarters, and years. Similarly, the \code{round}
and \code{trunc} methods for ITime's are useful for grouping and plotting.
It can round to weeks, months, quarters, and years. In the case of weeks,
a "special" argument \code{week_start} is available \emph{on certain R versions\
where the \code{round} generic allows this extension}. Unfortunately earlier
R versions (e.g. up through R 4.3.3) do not allow more than one argument to \code{round}.
Where available, this argument controls when the week starts. Like \code{wday()},
1 means Sunday and 7 means Saturday; character strings naming the weekday (in English)
are also converted. The default, \code{"Jan 1"}, means weeks should be aligned to the
beginning of the year. Note that this default implies dates coming at the end of the year
will wind up with fewer than 7 days mapping to the same rounded week start.

Similarly, the \code{round} and \code{trunc} methods for ITime's are useful for grouping and plotting.
They can round or truncate to hours and minutes.
Note for ITime's with 30 seconds, rounding is inconsistent due to rounding off a 5.
See 'Details' in \code{\link{round}} for more information.
Expand Down