Skip to content
Open
Show file tree
Hide file tree
Changes from 14 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
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@

6. `yearqtr()` and `yearmon()` now gain an optional format specifier [#7694](https://github.com/Rdatatable/data.table/issues/7694). 'numeric' is the default, which preserves the original behavior, but 'character' formats `yearqtr()` as YYYYQ# (e.g. 2025Q2) and `yearmon()` as YYYYM## (e.g. 2025M02, 2025M10). Thanks to @jan-swissre for the report and @LunaticSage218 for the implementation.

7. `print.data.table()` gains a `show.ncols` argument and `datatable.show.ncols` option to print the number of columns (labeled as `ncol:`) in the header, [#6663](https://github.com/Rdatatable/data.table/issues/6663). When `trunc.cols=TRUE`, this information is merged into the truncation message at the top, and the redundant footer message is suppressed. Thanks to @KyleHaynes for the suggestion and @venom1204 for the implementation.

### BUG FIXES

1. `fread()` with `skip=0` and `(header=TRUE|FALSE)` no longer skips the first row when it has fewer fields than subsequent rows, [#7463](https://github.com/Rdatatable/data.table/issues/7463). Thanks @emayerhofer for the report and @ben-schwen for the fix.
Expand Down
1 change: 1 addition & 0 deletions R/onLoad.R
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
datatable.print.keys=TRUE, # for print.data.table
datatable.print.trunc.cols=FALSE, # for print.data.table
datatable.show.indices=FALSE, # for print.data.table
datatable.show.ncols=FALSE, # for print.data.table
datatable.allow.cartesian=FALSE, # datatable.<argument name>
datatable.join.many=TRUE, # mergelist, [.data.table #4383 #914
datatable.dfdispatchwarn=TRUE, # not a function argument
Expand Down
42 changes: 33 additions & 9 deletions R/print.data.table.R
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ print.data.table = function(x, topn=getOption("datatable.print.topn"),
print.keys=getOption("datatable.print.keys"),
trunc.cols=getOption("datatable.print.trunc.cols"),
show.indices=getOption("datatable.show.indices"),
show.ncols=getOption("datatable.show.ncols", FALSE),
quote=FALSE,
na.print=NULL,
timezone=FALSE, ...) {
Expand Down Expand Up @@ -43,6 +44,7 @@ print.data.table = function(x, topn=getOption("datatable.print.topn"),
if (!is.numeric(topn)) topn = 5L
topnmiss = missing(topn)
topn = max(as.integer(topn),1L)
show_trunc_message = isTRUE(trunc.cols)
if (print.keys) {
if (!is.null(ky <- key(x)))
catf("Key: <%s>\n", toString(ky))
Expand All @@ -52,6 +54,9 @@ print.data.table = function(x, topn=getOption("datatable.print.topn"),
paste0("<", ixs, ">", collapse = ", ")
))
}
if (show.ncols && !isTRUE(trunc.cols) && !any(dim(x)==0L)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we move this below the any(dim(x)==0L) branch which comes next and drop the !any(dim(x)==0L) guard?

trunc_cols_message(character(0), NULL, FALSE, "none", ncol=ncol(x))
}
if (any(dim(x)==0L)) {
x_class = if (is.data.table(x)) "data.table" else "data.frame" # a data.frame could be passed to print.data.table() directly, #3363
if (all(dim(x)==0L)) {
Expand Down Expand Up @@ -121,8 +126,13 @@ print.data.table = function(x, topn=getOption("datatable.print.topn"),
cons_width = getOption("width")
cols_to_print = widths < cons_width
not_printed = colnames(toprint)[!cols_to_print]
if (show.ncols) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The column count seems to be wrong when using show.indices=TRUE

DT = data.table(a=1:3, b=1:3, c=1:3, d=1:3, e=1:3)
setindex(DT, a)
options(width=20, datatable.show.ncols=TRUE)
print(DT, trunc.cols=TRUE, show.indices=TRUE)
# Index: <a>
# Number of columns: 5, of which 4 are not shown: [c <int>, d <int>, e <int>, index:a <int>]
#        a     b
#    <int> <int>
# 1:     1     1
# 2:     2     2
# 3:     3     3

trunc_cols_message(not_printed, abbs, class, col.names, ncol=ncol(x))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be even as simple to exchange ncol(x) with ncol(toprint)

show_trunc_message = FALSE
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
}
}

wrong indentation


if (!any(cols_to_print)) {
trunc_cols_message(not_printed, abbs, class, col.names)
if (show_trunc_message) trunc_cols_message(not_printed, abbs, class, col.names)
return(invisible(x))
}
# When nrow(toprint) = 1, attributes get lost in the subset,
Expand All @@ -134,7 +144,7 @@ print.data.table = function(x, topn=getOption("datatable.print.topn"),
if (col.names != "none") cut_colnames = identity
cut_colnames(print(x, right=TRUE, quote=quote, na.print=na.print))
# prints names of variables not shown in the print
if (trunc.cols) trunc_cols_message(not_printed, abbs, class, col.names)
if (show_trunc_message) trunc_cols_message(not_printed, abbs, class, col.names)
}
if (printdots) {
if (isFALSE(row.names)) {
Expand Down Expand Up @@ -291,14 +301,28 @@ toprint_subset = function(x, cols_to_print) {
}
}
# message for when trunc.cols=TRUE and some columns are not printed
trunc_cols_message = function(not_printed, abbs, class, col.names){
trunc_cols_message = function(not_printed, abbs, class, col.names, ncol=NULL){

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we have some decent default arg values for trunc_cols_message then most call sites will become clearer and shorter

n = length(not_printed)
if (class && col.names != "none") classes = paste0(" ", tail(abbs, n)) else classes = ""
catf(
ngettext(n, "%d variable not shown: %s\n", "%d variables not shown: %s\n"),
n, brackify(paste0(not_printed, classes)),
domain=NA
)
if (is.null(ncol)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we make the branching here more clear with

if (n == 0L) {
  if (!is.null(ncol)) catf("Number of columns: %d\n", ncol)
  return()
}

if (n == 0L) return()
if (class && col.names != "none") classes = paste0(" ", tail(abbs, n)) else classes = ""
catf(
ngettext(n, "%d variable not shown: %s\n", "%d variables not shown: %s\n"),
n, brackify(paste0(not_printed, classes)),
domain=NA
)
} else {
if (n > 0L) {
if (class && col.names != "none") classes = paste0(" ", tail(abbs, n)) else classes = ""
catf(
ngettext(n, "Number of columns: %d, of which %d is not shown: %s\n", "Number of columns: %d, of which %d are not shown: %s\n"),
ncol, n, brackify(paste0(not_printed, classes)),
domain=NA
)
} else {
catf("Number of columns: %d\n", ncol)
}
}
}

# Maybe add a method for repr::repr_text. See https://github.com/Rdatatable/data.table/issues/933#issuecomment-220237965
Expand Down
10 changes: 10 additions & 0 deletions inst/tests/tests.Rraw
Original file line number Diff line number Diff line change
Expand Up @@ -21683,3 +21683,13 @@ max_ppsize = local({
x = as.data.table(as.list(1:max_ppsize))
test(2376, rbindlist(list(x)), x)
rm(x, max_ppsize)

#6663 Option to print the number of columns
test(2377.1, capture.output(print(as.data.table(iris)))[1L], "Number of columns: 5", options=list(datatable.show.ncols=TRUE))
test(2377.2, any(grepl("^Number of columns: 5$", capture.output(print(setkey(as.data.table(iris), Sepal.Length))))), TRUE, options=list(datatable.show.ncols=TRUE))
test(2377.3, any(grepl("^[0-9]+ variables? not shown", capture.output(print(data.table(a=1:3, b=1:3, c=1:3, d=1:3, e=1:3), trunc.cols=TRUE)))), FALSE, options=list(width=20, datatable.show.ncols=TRUE))
test(2377.4, capture.output(print(as.data.table(iris), trunc.cols=TRUE))[1L], "Number of columns: 5, of which 5 are not shown: [Sepal.Length, Sepal.Width, Petal.Length, Petal.Width, Species]", options=list(width=10, datatable.show.ncols=TRUE))
test(2377.5, any(grepl("^Number of columns:", capture.output(print(as.data.table(iris))))), FALSE, options=list(datatable.show.ncols=FALSE))
test(2377.6, capture.output(print(as.data.table(iris), trunc.cols=TRUE, class=TRUE))[1L], "Number of columns: 5, of which 5 are not shown: [Sepal.Length <num>, Sepal.Width <num>, Petal.Length <num>, Petal.Width <num>, Species <fctr>]", options=list(width=10, datatable.show.ncols=TRUE))
test(2377.7, { dt = data.table(matrix(1:200, nrow=25)); out = capture.output(print(dt, trunc.cols=TRUE, class=TRUE)); grepl("<int>", out[length(out)]) }, TRUE, options=list(width=30, datatable.show.ncols=TRUE))
test(2377.8, any(grepl("variable", capture.output(print(data.table(a=1), trunc.cols=TRUE)))), FALSE, options=list(width=80, datatable.show.ncols=FALSE))
1 change: 1 addition & 0 deletions man/data.table-options.Rd
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
\item{\code{datatable.print.keys}}{A logical, default \code{FALSE}. If \code{TRUE}, the table's
keys are printed above the data.}
\item{\code{datatable.show.indices}}{A logical, default \code{TRUE}. A synonym for \code{datatable.print.keys} for historical reasons.}
\item{\code{datatable.show.ncols}}{A logical, default \code{FALSE}. If \code{TRUE}, the number of columns is printed in the header.}
\item{\code{datatable.print.trunc.cols}}{A logical, default \code{FALSE}. If \code{TRUE} and a
table has more columns than fit on the screen, it truncates the middle columns.}
\item{\code{datatable.prettyprint.char}}{An integer, default \code{100L}. The maximum number of
Expand Down
2 changes: 2 additions & 0 deletions man/print.data.table.Rd
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
print.keys=getOption("datatable.print.keys"), # default: TRUE
trunc.cols=getOption("datatable.print.trunc.cols"), # default: FALSE
show.indices=getOption("datatable.show.indices"), # default: FALSE
show.ncols=getOption("datatable.show.ncols", FALSE), # default: FALSE
quote=FALSE,
na.print=NULL,
timezone=FALSE, \dots)
Expand All @@ -44,6 +45,7 @@
\item{trunc.char}{The number of characters at which character columns and list-column summaries are truncated. If \code{NULL} (the default), it is dynamically calculated based on \code{getOption("width")}.}
\item{class}{ If \code{TRUE}, the resulting output will include above each column its storage class (or a self-evident abbreviation thereof). When combined with \code{col.names="auto"} and tables >20 rows, classes will also appear at the bottom.}
\item{row.names}{ If \code{TRUE}, row indices will be printed alongside \code{x}. }
\item{show.ncols}{ If \code{TRUE}, the number of columns is printed in the header. }
\item{col.names}{ One of three flavours for controlling the display of column names in output. \code{"auto"} includes column names above the data, as well as below the table if \code{nrow(x) > 20} (when \code{class=TRUE}, column classes will also appear at the bottom). \code{"top"} excludes this lower register when applicable, and \code{"none"} suppresses column names altogether (as well as column classes if \code{class = TRUE}. }
\item{print.keys}{ If \code{TRUE}, any \code{\link{key}} and/or \code{\link[=indices]{index}} currently assigned to \code{x} will be printed prior to the preview of the data. }
\item{trunc.cols}{ If \code{TRUE}, only the columns that can be printed in the console without wrapping the columns to new lines will be printed (similar to \code{tibbles}). }
Expand Down
Loading