diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6bde4d405..91d1a9849 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -194,6 +194,7 @@ test-lin-dev-clang-cran: - echo 'CFLAGS=-g -O2 -fno-common -Wall -Wvla -pedantic -fstack-protector-strong -D_FORTIFY_SOURCE=2' > ~/.R/Makevars - echo 'CXXFLAGS=-g -O2 -fno-common -Wall -Wvla -pedantic -fstack-protector-strong -D_FORTIFY_SOURCE=2' >> ~/.R/Makevars - *install-deps + - clang-tidy -extra-arg=-I/usr/local/lib/R/include -checks='readability-inconsistent-declaration-parameter' src/*.c -- -std=c99 - R CMD check --as-cran $(ls -1t data.table_*.tar.gz | head -n 1) - (! grep "warning:" data.table.Rcheck/00install.out) - >- diff --git a/DESCRIPTION b/DESCRIPTION index 87a18c742..7447fed2a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -99,5 +99,6 @@ Authors@R: c( person("Elise", "Maigné", role="ctb"), person("Vincent", "Rocher", role="ctb"), person("Vijay", "Lulla", role="ctb"), - person("Aljaž", "Sluga", role="ctb") + person("Aljaž", "Sluga", role="ctb"), + person("Bill", "Evans", role="ctb") ) diff --git a/NAMESPACE b/NAMESPACE index 6e6343c1d..2497f0cf9 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -202,6 +202,7 @@ S3method(format_col, POSIXct) S3method(format_col, expression) export(format_list_item) S3method(format_list_item, default) +S3method(format_list_item, data.frame) export(fdroplevels, setdroplevels) S3method(droplevels, data.table) diff --git a/NEWS.md b/NEWS.md index 23213934f..53f37d478 100644 --- a/NEWS.md +++ b/NEWS.md @@ -113,6 +113,10 @@ rowwiseDT( 13. `rbindlist(l, use.names=TRUE)` can now handle different encodings for the column names in different entries of `l`, [#5452](https://github.com/Rdatatable/data.table/issues/5452). Thanks to @MEO265 for the report, and Benjamin Schwendinger for the fix. +14. Added a `data.frame` method for `format_list_item()` to fix error printing data.tables with columns containing 1-column data.frames, [#6592](https://github.com/Rdatatable/data.table/issues/6592). Thanks to @r2evans for the bug report and fix. + +15. The auto-printing suppression in `knitr` documents is now done by implementing a method for `knit_print` instead of looking up the call stack, [#6589](https://github.com/Rdatatable/data.table/pull/6589). Thanks to @jangorecki for the report [#6509](https://github.com/Rdatatable/data.table/issues/6509) and @aitap for the fix. + ## NOTES 1. Tests run again when some Suggests packages are missing, [#6411](https://github.com/Rdatatable/data.table/issues/6411). Thanks @aadler for the note and @MichaelChirico for the fix. diff --git a/R/onLoad.R b/R/onLoad.R index 026327cd9..662f3132e 100644 --- a/R/onLoad.R +++ b/R/onLoad.R @@ -66,15 +66,14 @@ lockBinding("rbind.data.frame",baseenv()) } } - if (session_r_version < "3.6.0") { + if (session_r_version < "3.6.0") { # corresponds to S3method() directive in NAMESPACE # no delayed registration support for NAMESPACE; perform it manually if (isNamespaceLoaded("knitr")) { registerS3method("knit_print", "data.table", knit_print.data.table, envir = asNamespace("knitr")) - } else { - setHook(packageEvent("knitr", "onLoad"), function(...) { - registerS3method("knit_print", "data.table", knit_print.data.table, envir = asNamespace("knitr")) - }) } + setHook(packageEvent("knitr", "onLoad"), function(...) { + registerS3method("knit_print", "data.table", knit_print.data.table, envir = asNamespace("knitr")) + }) } # Set options for the speed boost in v1.8.0 by avoiding 'default' arg of getOption(,default=) diff --git a/R/print.data.table.R b/R/print.data.table.R index 525aebe93..4116f20a6 100644 --- a/R/print.data.table.R +++ b/R/print.data.table.R @@ -231,6 +231,11 @@ format_list_item.default = function(x, ...) { } } +# #6592 -- nested 1-column frames breaks printing +format_list_item.data.frame = function(x, ...) { + paste0("<", class1(x), paste_dims(x), ">") +} + # FR #1091 for pretty printing of character # TODO: maybe instead of doing "this is...", we could do "this ... test"? # Current implementation may have issues when dealing with strings that have combinations of full-width and half-width characters, diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index 451dad684..4b4cddf73 100644 --- a/inst/tests/tests.Rraw +++ b/inst/tests/tests.Rraw @@ -20651,3 +20651,16 @@ test(2298.2, rbindlist(list(y,x), use.names=TRUE), data.table("\u00f6"=c(4,2), " set(y, j="\u00e4", value=NULL) test(2298.3, rbindlist(list(x,y), use.names=TRUE, fill=TRUE), data.table("\u00e4"=c(1,NA), "\u00f6"=c(2,4), "\u00fc"=c(3,5))) test(2298.4, rbindlist(list(y,x), use.names=TRUE, fill=TRUE), data.table("\u00f6"=c(4,2), "\u00fc"=c(5,3), "\u00e4"=c(NA,1))) + +# #6592: printing nested single-column frames +test(2299.01, format_list_item(data.frame(a=1)), output="") +test(2299.02, format_list_item(data.frame(a=1)[0,,drop=FALSE]), output="") +test(2299.03, format_list_item(data.frame(a=1)[,0]), output="") +test(2299.04, format_list_item(data.frame(a=1, b=2)[0,,drop=FALSE]), output="") +test(2299.06, format_list_item(data.table(a=1)), output="") +test(2299.07, format_list_item(data.table(a=numeric())), output="") +test(2299.08, format_list_item(data.table()), output="") +test(2299.09, format_list_item(data.table(a=numeric(), b=numeric())), output="") +test(2299.10, data.table(a=1), output="a\n1: *1") +test(2299.11, data.table(a=list(data.frame(b=1))), output="a\n1: ") +test(2299.12, data.table(a=list(data.table(b=1))), output="a\n1: ") diff --git a/tests/autoprint.R b/tests/autoprint.R index b752de6cc..f6f0433db 100644 --- a/tests/autoprint.R +++ b/tests/autoprint.R @@ -44,6 +44,18 @@ tryCatch(DT[,foo:=ColumnNameTypo], error=function(e) e$message) # error: DT # yes DT # yes +# Regression test for auto-printing suppression in source(), #2369 +local({ + f = tempfile(fileext = ".R") + on.exit(unlink(f)) + writeLines(c( + "library(data.table)", + "DT = data.table(a = 1)", + "DT[,a:=1]" # no + ), f) + source(f, local = TRUE, echo = TRUE) +}) + # child class of data.table doesn't induce unintended print, #3029 dt <- data.table(x = 1) class(dt) <- c("foo", "data.table", "data.frame") diff --git a/tests/autoprint.Rout.save b/tests/autoprint.Rout.save index 19d5b801a..48a5533ee 100644 --- a/tests/autoprint.Rout.save +++ b/tests/autoprint.Rout.save @@ -152,6 +152,23 @@ NULL 1: 10 2: 10 > +> # Regression test for auto-printing suppression in source(), #2369 +> local({ ++ f = tempfile(fileext = ".R") ++ on.exit(unlink(f)) ++ writeLines(c( ++ "library(data.table)", ++ "DT = data.table(a = 1)", ++ "DT[,a:=1]" # no ++ ), f) ++ source(f, local = TRUE, echo = TRUE) ++ }) + +> library(data.table) + +> DT = data.table(a = 1) + +> DT[, `:=`(a, 1)] > # child class of data.table doesn't induce unintended print, #3029 > dt <- data.table(x = 1) > class(dt) <- c("foo", "data.table", "data.frame") diff --git a/vignettes/datatable-intro.Rmd b/vignettes/datatable-intro.Rmd index d32a25eb9..9bc4d027c 100644 --- a/vignettes/datatable-intro.Rmd +++ b/vignettes/datatable-intro.Rmd @@ -643,6 +643,26 @@ DT[, print(list(c(a,b))), by = ID] # (2) In (1), for each group, a vector is returned, with length = 6,4,2 here. However, (2) returns a list of length 1 for each group, with its first element holding vectors of length 6,4,2. Therefore, (1) results in a length of ` 6+4+2 = `r 6+4+2``, whereas (2) returns `1+1+1=`r 1+1+1``. +Flexibility of j allows us to store any list object as an element of data.table. For example, when statistical models are fit to groups, these models can be stored in a data.table. Code is concise and easy to understand. + +```{r} +## Do long distance flights cover up departure delay more than short distance flights? +## Does cover up vary by month? +flights[, `:=`(makeup = dep_delay - arr_delay)] + +makeup.models <- flights[, .(fit = list(lm(makeup ~ distance))), by = .(month)] +makeup.models[, .(coefdist = coef(fit[[1]])[2], rsq = summary(fit[[1]])$r.squared), by = .(month)] +``` +Using data.frames, we need more complicated code to obtain same result. +```{r} +setDF(flights) +flights.split <- split(flights, f = flights$month) +makeup.models.list <- lapply(flights.split, function(df) c(month = df$month[1], fit = list(lm(makeup ~ distance, data = df)))) +makeup.models.df <- do.call(rbind, makeup.models.list) +sapply(makeup.models.df[, "fit"], function(model) c(coefdist = coef(model)[2], rsq = summary(model)$r.squared)) |> t() |> data.frame() +setDT(flights) +``` + ## Summary The general form of `data.table` syntax is: