From ae1b780ee99cf9e413220b06644ba678adb21353 Mon Sep 17 00:00:00 2001 From: Benjamin Schwendinger <52290390+ben-schwen@users.noreply.github.com> Date: Mon, 19 Dec 2022 14:54:19 +0100 Subject: [PATCH 01/13] copy classes to grouping vars --- R/data.table.R | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/R/data.table.R b/R/data.table.R index 473cf6e76..2140994ab 100644 --- a/R/data.table.R +++ b/R/data.table.R @@ -1850,6 +1850,12 @@ replace_dot_alias = function(e) { ans = gforce(thisEnv, jsub, o__, f__, len__, irows) # irows needed for #971. gi = if (length(o__)) o__[f__] else f__ g = lapply(grpcols, function(i) groups[[i]][gi]) + cl = lapply(groups, class) + if (vapply_1b(cl, function(x) length(x)>1L)) { # copy classes for grouping variables #5567 + for (i in seq_along(g)) { + setattr(g[[i]], 'class', cl[[i]]) + } + } # returns all rows instead of one per group nrow_funs = c("gshift") From 39ef816936efc0a6ae2ae62bec59de72aad8bdc2 Mon Sep 17 00:00:00 2001 From: Benjamin Schwendinger <52290390+ben-schwen@users.noreply.github.com> Date: Mon, 19 Dec 2022 21:16:42 +0100 Subject: [PATCH 02/13] add tests --- R/data.table.R | 8 +------- inst/tests/tests.Rraw | 5 +++++ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/R/data.table.R b/R/data.table.R index 2140994ab..17ac209cd 100644 --- a/R/data.table.R +++ b/R/data.table.R @@ -1849,13 +1849,7 @@ replace_dot_alias = function(e) { if (use.I) assign(".I", seq_len(nrow(x)), thisEnv) ans = gforce(thisEnv, jsub, o__, f__, len__, irows) # irows needed for #971. gi = if (length(o__)) o__[f__] else f__ - g = lapply(grpcols, function(i) groups[[i]][gi]) - cl = lapply(groups, class) - if (vapply_1b(cl, function(x) length(x)>1L)) { # copy classes for grouping variables #5567 - for (i in seq_along(g)) { - setattr(g[[i]], 'class', cl[[i]]) - } - } + g = lapply(grpcols, function(i) .Call(CsubsetVector, groups[[i]], gi)) # use CsubsetVector instead of [ since this preserves attributes #5567 # returns all rows instead of one per group nrow_funs = c("gshift") diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index 3cbe67680..6e55cdcf5 100644 --- a/inst/tests/tests.Rraw +++ b/inst/tests/tests.Rraw @@ -18094,3 +18094,8 @@ test(2238.6, "a" %notin% integer(), TRUE) test(2238.7, "a" %notin% NULL, TRUE) test(2238.8, NA %notin% 1:5, TRUE) test(2238.9, NA %notin% c(1:5, NA), FALSE) + +# GForce drops classes in by arguments #5567 +dt = data.table(a=letters[1:4], b=structure(1:4, class = c("class_b", "integer")), c=structure(c(1L,2L,1L,2L), class = c("class_c", "integer"))) +test(2239.1, dt[, .N, b], data.table(b=dt$b, N=1L)) +test(2239.2, dt[, .N, .(b,c)], data.table(b=dt$b, c=dt$c, N=1L)) From 86962f5f820ab4ead87280f4464ef71122a0b679 Mon Sep 17 00:00:00 2001 From: Benjamin Schwendinger <52290390+ben-schwen@users.noreply.github.com> Date: Mon, 19 Dec 2022 21:20:54 +0100 Subject: [PATCH 03/13] add different optimization levels to test --- inst/tests/tests.Rraw | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index 6e55cdcf5..e5c212eef 100644 --- a/inst/tests/tests.Rraw +++ b/inst/tests/tests.Rraw @@ -18097,5 +18097,9 @@ test(2238.9, NA %notin% c(1:5, NA), FALSE) # GForce drops classes in by arguments #5567 dt = data.table(a=letters[1:4], b=structure(1:4, class = c("class_b", "integer")), c=structure(c(1L,2L,1L,2L), class = c("class_c", "integer"))) -test(2239.1, dt[, .N, b], data.table(b=dt$b, N=1L)) -test(2239.2, dt[, .N, .(b,c)], data.table(b=dt$b, c=dt$c, N=1L)) +options(datatable.optimize=0) +test(2239.1, dt[, .N, b], data.table(b=dt$b, N=1L), output="GForce FALSE") +test(2239.2, dt[, .N, .(b,c)], data.table(b=dt$b, c=dt$c, N=1L), output="GForce FALSE") +options(datatable.optimize=Inf) +test(2239.3, dt[, .N, b], data.table(b=dt$b, N=1L), output="GForce TRUE") +test(2239.4, dt[, .N, .(b,c)], data.table(b=dt$b, c=dt$c, N=1L), output="GForce TRUE") From be5dabdaa167595e9a483ddc9fe5185090cf7d1f Mon Sep 17 00:00:00 2001 From: Benjamin Schwendinger <52290390+ben-schwen@users.noreply.github.com> Date: Mon, 19 Dec 2022 21:26:12 +0100 Subject: [PATCH 04/13] add news --- NEWS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS.md b/NEWS.md index 06c6013fe..af62ea457 100644 --- a/NEWS.md +++ b/NEWS.md @@ -561,6 +561,8 @@ identical(DT1, DT2) # TRUE ``` +55. `DT[,j,by]` could loose `class` attributes of `by` when `j` was GForce optimized, [#5567](https://github.com/Rdatatable/data.table/issues/5567). Thanks to @danwwilson for the report, and @ben-schwen for the PR. + ## NOTES 1. New feature 29 in v1.12.4 (Oct 2019) introduced zero-copy coercion. Our thinking is that requiring you to get the type right in the case of `0` (type double) vs `0L` (type integer) is too inconvenient for you the user. So such coercions happen in `data.table` automatically without warning. Thanks to zero-copy coercion there is no speed penalty, even when calling `set()` many times in a loop, so there's no speed penalty to warn you about either. However, we believe that assigning a character value such as `"2"` into an integer column is more likely to be a user mistake that you would like to be warned about. The type difference (character vs integer) may be the only clue that you have selected the wrong column, or typed the wrong variable to be assigned to that column. For this reason we view character to numeric-like coercion differently and will warn about it. If it is correct, then the warning is intended to nudge you to wrap the RHS with `as.()` so that it is clear to readers of your code that a coercion from character to that type is intended. For example : From 5aa5e645e1b7cf6ef17e853707eeb302fb85d695 Mon Sep 17 00:00:00 2001 From: Benjamin Schwendinger <52290390+ben-schwen@users.noreply.github.com> Date: Mon, 19 Dec 2022 22:00:13 +0100 Subject: [PATCH 05/13] add output --- inst/tests/tests.Rraw | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index e5c212eef..0d865ebf6 100644 --- a/inst/tests/tests.Rraw +++ b/inst/tests/tests.Rraw @@ -18097,9 +18097,11 @@ test(2238.9, NA %notin% c(1:5, NA), FALSE) # GForce drops classes in by arguments #5567 dt = data.table(a=letters[1:4], b=structure(1:4, class = c("class_b", "integer")), c=structure(c(1L,2L,1L,2L), class = c("class_c", "integer"))) +old = options(datatable.verbose=TRUE) options(datatable.optimize=0) -test(2239.1, dt[, .N, b], data.table(b=dt$b, N=1L), output="GForce FALSE") +test(2239.1, dt[, .N, b], data.table(b=dt$b, N=1L), output="GForce FALSE") test(2239.2, dt[, .N, .(b,c)], data.table(b=dt$b, c=dt$c, N=1L), output="GForce FALSE") options(datatable.optimize=Inf) -test(2239.3, dt[, .N, b], data.table(b=dt$b, N=1L), output="GForce TRUE") -test(2239.4, dt[, .N, .(b,c)], data.table(b=dt$b, c=dt$c, N=1L), output="GForce TRUE") +test(2239.3, dt[, .N, b], data.table(b=dt$b, N=1L), output="GForce optimized j to") +test(2239.4, dt[, .N, .(b,c)], data.table(b=dt$b, c=dt$c, N=1L), output="GForce optimized j to") +options(old) From 6faf43d852ca04b50adb992e1382d276ad9506b4 Mon Sep 17 00:00:00 2001 From: Benjamin Schwendinger Date: Fri, 17 May 2024 23:34:23 +0200 Subject: [PATCH 06/13] fix news --- NEWS.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/NEWS.md b/NEWS.md index 5f09091e8..a35cf62a2 100644 --- a/NEWS.md +++ b/NEWS.md @@ -58,6 +58,8 @@ 7. `melt` returns an integer column for `variable` when `measure.vars` is a list of length=1, consistent with the documented behavior, [#5209](https://github.com/Rdatatable/data.table/issues/5209). Thanks to @tdhock for reporting and fixing. Any users who were relying on this behavior can change `measure.vars=list("col_name")` (output `variable` was column name, now is column index/integer) to `measure.vars="col_name"` (`variable` still is column name). +8. `DT[,j,by]` could loose `class` attributes of `by` when `j` was GForce optimized, [#5567](https://github.com/Rdatatable/data.table/issues/5567). Thanks to @danwwilson for the report, and @ben-schwen for the PR. + ## NOTES 1. `transform` method for data.table sped up substantially when creating new columns on large tables. Thanks to @OfekShilon for the report and PR. The implemented solution was proposed by @ColeMiller1. @@ -648,13 +650,9 @@ identical(DT1, DT2) # TRUE ``` -<<<<<<< HEAD -55. `DT[,j,by]` could loose `class` attributes of `by` when `j` was GForce optimized, [#5567](https://github.com/Rdatatable/data.table/issues/5567). Thanks to @danwwilson for the report, and @ben-schwen for the PR. -======= 55. `fread(URL)` with `https:` and `ftps:` could timeout if proxy settings were not guessed right by `curl::curl_download`, [#1686](https://github.com/Rdatatable/data.table/issues/1686). `fread(URL)` now uses `download.file()` as default for downloading files from urls. Thanks to @cderv for the report and Benjamin Schwendinger for the fix. 56. `split.data.table()` works for downstream methods that don't implement `DT[i]` form (i.e., requiring `DT[i, j]` form, like plain `data.frame`s), for example `sf`'s `[.sf`, [#5365](https://github.com/Rdatatable/data.table/issues/5365). Thanks @barryrowlingson for the report and @michaelchirico for the fix. ->>>>>>> master ## NOTES From fe83896c169958790b668983dd3fd566a0930f14 Mon Sep 17 00:00:00 2001 From: Benjamin Schwendinger Date: Fri, 17 May 2024 23:43:00 +0200 Subject: [PATCH 07/13] fix typo --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index a35cf62a2..8fa461af6 100644 --- a/NEWS.md +++ b/NEWS.md @@ -58,7 +58,7 @@ 7. `melt` returns an integer column for `variable` when `measure.vars` is a list of length=1, consistent with the documented behavior, [#5209](https://github.com/Rdatatable/data.table/issues/5209). Thanks to @tdhock for reporting and fixing. Any users who were relying on this behavior can change `measure.vars=list("col_name")` (output `variable` was column name, now is column index/integer) to `measure.vars="col_name"` (`variable` still is column name). -8. `DT[,j,by]` could loose `class` attributes of `by` when `j` was GForce optimized, [#5567](https://github.com/Rdatatable/data.table/issues/5567). Thanks to @danwwilson for the report, and @ben-schwen for the PR. +8. `DT[,j,by]` could lose `class` attributes of `by` when `j` was GForce optimized, [#5567](https://github.com/Rdatatable/data.table/issues/5567). Thanks to @danwwilson for the report, and @ben-schwen for the PR. ## NOTES From 9752f9388adb1a8dcb8a537bcadcd3416638f75d Mon Sep 17 00:00:00 2001 From: Benjamin Schwendinger Date: Sun, 19 May 2024 11:46:20 +0200 Subject: [PATCH 08/13] add NEWS info and tests about attributes --- NEWS.md | 2 +- inst/tests/tests.Rraw | 18 ++++++++---------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/NEWS.md b/NEWS.md index 8fa461af6..b5cf10bad 100644 --- a/NEWS.md +++ b/NEWS.md @@ -58,7 +58,7 @@ 7. `melt` returns an integer column for `variable` when `measure.vars` is a list of length=1, consistent with the documented behavior, [#5209](https://github.com/Rdatatable/data.table/issues/5209). Thanks to @tdhock for reporting and fixing. Any users who were relying on this behavior can change `measure.vars=list("col_name")` (output `variable` was column name, now is column index/integer) to `measure.vars="col_name"` (`variable` still is column name). -8. `DT[,j,by]` could lose `class` attributes of `by` when `j` was GForce optimized, [#5567](https://github.com/Rdatatable/data.table/issues/5567). Thanks to @danwwilson for the report, and @ben-schwen for the PR. +8. In `DT[,j,by]`, `by` retains now its attributes when `j` is GForce optimized, [#5567](https://github.com/Rdatatable/data.table/issues/5567). Thanks to @danwwilson for the report, and @ben-schwen for the PR. ## NOTES diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index 963d9c82d..1ebe7226c 100644 --- a/inst/tests/tests.Rraw +++ b/inst/tests/tests.Rraw @@ -18567,13 +18567,11 @@ test(2261.04, setNumericRounding(2L), 1L) test(2261.05, capture.output(setNumericRounding(2L)), character(0)) setNumericRounding(old) -# GForce drops classes in by arguments #5567 -dt = data.table(a=letters[1:4], b=structure(1:4, class = c("class_b", "integer")), c=structure(c(1L,2L,1L,2L), class = c("class_c", "integer"))) -old = options(datatable.verbose=TRUE) -options(datatable.optimize=0) -test(2262.1, dt[, .N, b], data.table(b=dt$b, N=1L), output="GForce FALSE") -test(2262.2, dt[, .N, .(b,c)], data.table(b=dt$b, c=dt$c, N=1L), output="GForce FALSE") -options(datatable.optimize=Inf) -test(2262.3, dt[, .N, b], data.table(b=dt$b, N=1L), output="GForce optimized j to") -test(2262.4, dt[, .N, .(b,c)], data.table(b=dt$b, c=dt$c, N=1L), output="GForce optimized j to") -options(old) +# GForce drops attributes in by arguments #5567 +dt = data.table(a=letters[1:4], b=structure(1:4, class = c("class_b", "integer"), att=1), c=structure(c(1L,2L,1L,2L), class = c("class_c", "integer"))) +test(2262.1, options=list(datatable.verbose=TRUE, datatable.optimize=0L), dt[, .N, b], data.table(b=dt$b, N=1L), output="GForce FALSE") +test(2262.2, options=list(datatable.verbose=TRUE, datatable.optimize=0L), dt[, .N, .(b,c)], data.table(b=dt$b, c=dt$c, N=1L), output="GForce FALSE") +test(2262.3, options=list(datatable.verbose=TRUE, datatable.optimize=0L), names(attributes(dt[, .N, b][,b])), c("class", "att"), output="GForce FALSE") +test(2262.4, options=list(datatable.verbose=TRUE, datatable.optimize=Inf), dt[, .N, b], data.table(b=dt$b, N=1L), output="GForce optimized j to") +test(2262.5, options=list(datatable.verbose=TRUE, datatable.optimize=Inf), dt[, .N, .(b,c)], data.table(b=dt$b, c=dt$c, N=1L), output="GForce optimized j to") +test(2262.6, options=list(datatable.verbose=TRUE, datatable.optimize=Inf), names(attributes(dt[, .N, b][,b])), c("class", "att"), output="GForce optimized j to") From d051b5e8da8e4eb76049199e278fc4c027960ffe Mon Sep 17 00:00:00 2001 From: Michael Chirico Date: Mon, 20 May 2024 10:17:25 -0700 Subject: [PATCH 09/13] hone NEWS --- NEWS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index b5cf10bad..ba5dcdbde 100644 --- a/NEWS.md +++ b/NEWS.md @@ -40,7 +40,7 @@ 12. `setDT` is faster for data with many columns, thanks @MichaelChirico for reporting and fixing the issue, [#5426](https://github.com/Rdatatable/data.table/issues/5426). -13. `dcast`gains `value.var.in.dots`, `value.var.in.LHSdots` and `value.var.in.RHSdots` arguments, [#5824](https://github.com/Rdatatable/data.table/issues/5824). This allows the `value.var` variable(s) in `dcast` to be represented by `...` in the formula (if not otherwise mentioned). Thanks to @iago-pssjd for the report and PR. +13. `dcast` gains `value.var.in.dots`, `value.var.in.LHSdots` and `value.var.in.RHSdots` arguments, [#5824](https://github.com/Rdatatable/data.table/issues/5824). This allows the `value.var` variable(s) in `dcast` to be represented by `...` in the formula (if not otherwise mentioned). Thanks to @iago-pssjd for the report and PR. ## BUG FIXES @@ -58,7 +58,7 @@ 7. `melt` returns an integer column for `variable` when `measure.vars` is a list of length=1, consistent with the documented behavior, [#5209](https://github.com/Rdatatable/data.table/issues/5209). Thanks to @tdhock for reporting and fixing. Any users who were relying on this behavior can change `measure.vars=list("col_name")` (output `variable` was column name, now is column index/integer) to `measure.vars="col_name"` (`variable` still is column name). -8. In `DT[,j,by]`, `by` retains now its attributes when `j` is GForce optimized, [#5567](https://github.com/Rdatatable/data.table/issues/5567). Thanks to @danwwilson for the report, and @ben-schwen for the PR. +8. In `DT[,j,by]`, `by` retains its attributes (e.g. class) when `j` is GForce optimized, [#5567](https://github.com/Rdatatable/data.table/issues/5567). Thanks to @danwwilson for the report, and @ben-schwen for the PR. ## NOTES From 95c17fbd5dddd58b3f3ab494f9c24ed3a7fb2e3e Mon Sep 17 00:00:00 2001 From: Michael Chirico Date: Mon, 20 May 2024 10:18:08 -0700 Subject: [PATCH 10/13] hone comment --- R/data.table.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/data.table.R b/R/data.table.R index d7e960815..7975d2a3a 100644 --- a/R/data.table.R +++ b/R/data.table.R @@ -1842,7 +1842,7 @@ replace_dot_alias = function(e) { if (use.I) assign(".I", seq_len(nrow(x)), thisEnv) ans = gforce(thisEnv, jsub, o__, f__, len__, irows) # irows needed for #971. gi = if (length(o__)) o__[f__] else f__ - g = lapply(grpcols, function(i) .Call(CsubsetVector, groups[[i]], gi)) # use CsubsetVector instead of [ since this preserves attributes #5567 + g = lapply(grpcols, function(i) .Call(CsubsetVector, groups[[i]], gi)) # use CsubsetVector instead of [ to preserve attributes #5567 # returns all rows instead of one per group nrow_funs = c("gshift") From 8b2861dd46a45c78bd77b95d059468d49484eeff Mon Sep 17 00:00:00 2001 From: Michael Chirico Date: Mon, 20 May 2024 10:18:51 -0700 Subject: [PATCH 11/13] Reframe test annotation --- inst/tests/tests.Rraw | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index f9654eafd..6bdfc9ee0 100644 --- a/inst/tests/tests.Rraw +++ b/inst/tests/tests.Rraw @@ -18567,7 +18567,7 @@ test(2261.04, setNumericRounding(2L), 1L) test(2261.05, capture.output(setNumericRounding(2L)), character(0)) setNumericRounding(old) -# GForce drops attributes in by arguments #5567 +# GForce retains attributes in by arguments #5567 dt = data.table(a=letters[1:4], b=structure(1:4, class = c("class_b", "integer"), att=1), c=structure(c(1L,2L,1L,2L), class = c("class_c", "integer"))) test(2262.1, options=list(datatable.verbose=TRUE, datatable.optimize=0L), dt[, .N, b], data.table(b=dt$b, N=1L), output="GForce FALSE") test(2262.2, options=list(datatable.verbose=TRUE, datatable.optimize=0L), dt[, .N, .(b,c)], data.table(b=dt$b, c=dt$c, N=1L), output="GForce FALSE") From 1556797fec6309972109a60551e71604ac6c23f0 Mon Sep 17 00:00:00 2001 From: Michael Chirico Date: Mon, 20 May 2024 10:20:19 -0700 Subject: [PATCH 12/13] tweak test --- inst/tests/tests.Rraw | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index 6bdfc9ee0..22819863a 100644 --- a/inst/tests/tests.Rraw +++ b/inst/tests/tests.Rraw @@ -18574,4 +18574,4 @@ test(2262.2, options=list(datatable.verbose=TRUE, datatable.optimize=0L), dt[, . test(2262.3, options=list(datatable.verbose=TRUE, datatable.optimize=0L), names(attributes(dt[, .N, b][,b])), c("class", "att"), output="GForce FALSE") test(2262.4, options=list(datatable.verbose=TRUE, datatable.optimize=Inf), dt[, .N, b], data.table(b=dt$b, N=1L), output="GForce optimized j to") test(2262.5, options=list(datatable.verbose=TRUE, datatable.optimize=Inf), dt[, .N, .(b,c)], data.table(b=dt$b, c=dt$c, N=1L), output="GForce optimized j to") -test(2262.6, options=list(datatable.verbose=TRUE, datatable.optimize=Inf), names(attributes(dt[, .N, b][,b])), c("class", "att"), output="GForce optimized j to") +test(2262.6, options=list(datatable.verbose=TRUE, datatable.optimize=Inf), names(attributes(dt[, .N, b]$b)), c("class", "att"), output="GForce optimized j to") From 918d9624ec9c44ea57566a8a64e88a5c182132cc Mon Sep 17 00:00:00 2001 From: Michael Chirico Date: Mon, 20 May 2024 10:21:43 -0700 Subject: [PATCH 13/13] Second call site --- inst/tests/tests.Rraw | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index 22819863a..c974f83a0 100644 --- a/inst/tests/tests.Rraw +++ b/inst/tests/tests.Rraw @@ -18571,7 +18571,7 @@ setNumericRounding(old) dt = data.table(a=letters[1:4], b=structure(1:4, class = c("class_b", "integer"), att=1), c=structure(c(1L,2L,1L,2L), class = c("class_c", "integer"))) test(2262.1, options=list(datatable.verbose=TRUE, datatable.optimize=0L), dt[, .N, b], data.table(b=dt$b, N=1L), output="GForce FALSE") test(2262.2, options=list(datatable.verbose=TRUE, datatable.optimize=0L), dt[, .N, .(b,c)], data.table(b=dt$b, c=dt$c, N=1L), output="GForce FALSE") -test(2262.3, options=list(datatable.verbose=TRUE, datatable.optimize=0L), names(attributes(dt[, .N, b][,b])), c("class", "att"), output="GForce FALSE") +test(2262.3, options=list(datatable.verbose=TRUE, datatable.optimize=0L), names(attributes(dt[, .N, b]$b)), c("class", "att"), output="GForce FALSE") test(2262.4, options=list(datatable.verbose=TRUE, datatable.optimize=Inf), dt[, .N, b], data.table(b=dt$b, N=1L), output="GForce optimized j to") test(2262.5, options=list(datatable.verbose=TRUE, datatable.optimize=Inf), dt[, .N, .(b,c)], data.table(b=dt$b, c=dt$c, N=1L), output="GForce optimized j to") test(2262.6, options=list(datatable.verbose=TRUE, datatable.optimize=Inf), names(attributes(dt[, .N, b]$b)), c("class", "att"), output="GForce optimized j to")