Skip to content

Commit f85272b

Browse files
Merge remote-tracking branch 'origin/master' into rbindlist_encodings
2 parents efaf11b + 23dac21 commit f85272b

32 files changed

+4182
-102
lines changed

.Rbuildignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.dir-locals.el
2+
.check.translations.R
23
^\.Rprofile$
34
^data\.table_.*\.tar\.gz$
45
^config\.log$

.ci/.lintr.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ exclusions = c(local({
8484
infix_spaces_linter = Inf,
8585
undesirable_function_linter = Inf
8686
)),
87-
exclusion_for_dir("vignettes", list(
87+
exclusion_for_dir(c("vignettes", "vignettes/fr"), list(
8888
quotes_linter = Inf,
8989
sample_int_linter = Inf
9090
# strings_as_factors_linter = Inf

.github/workflows/pkgup.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ jobs:
6565
Rscript -e 'tools::write_PACKAGES("public/src/contrib", fields="Revision")'
6666
- name: upload
6767
if: github.ref == 'refs/heads/master'
68-
uses: actions/upload-pages-artifact@v4
68+
uses: actions/upload-pages-artifact@v3
6969
with:
7070
path: "public"
7171
- name: deploy

.gitlab-ci.yml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,6 @@ integration:
315315
- R --version
316316
- *install-deps ## markdown pkg not present in r-pkgdown image
317317
- mkdir -p ./pkgdown/favicon/ && cp .graphics/favicon/* ./pkgdown/favicon/ ## copy favicons
318-
- rm -rf ./vignettes ## r-lib/pkgdown#2383
319318
- Rscript -e 'pkgdown::build_site(override=list(destination="./website"))'
320319
## html manual, vignettes, repos, cran_web, cran_checks
321320
- echo 'source(".ci/ci.R"); source(".ci/publish.R")' >> .Rprofile
@@ -388,10 +387,6 @@ integration:
388387
- Rscript -e 'check.index("data.table", names(test.jobs))'
389388
## web/checks/check_flavors.html
390389
- Rscript -e 'check.flavors(names(test.jobs))'
391-
## pkgdown vignettes workaround r-lib/pkgdown#2383
392-
- mkdir -p website/articles
393-
- cp bus/integration/cran/library/data.table/doc/*.html website/articles/.
394-
- rm website/articles/index.html
395390
## pkgdown merge
396391
- Rscript -e 'common_files<-function(path1, path2) intersect(list.files(path1, all.files=TRUE, no..=TRUE), list.files(path2, all.files=TRUE, no..=TRUE)); msg = if (length(f<-common_files("website","bus/integration/cran"))) paste(c("Following artifacts will be overwritten by pkgdown artifacts:", paste0(" ", f)), collapse="\n") else "No overlapping files from pkgdown artifacts"; message(msg); q("no")'
397392
- mv website/* bus/integration/cran/

DESCRIPTION

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,5 +92,11 @@ Authors@R: c(
9292
person("Ivan", "Krylov", role="ctb"),
9393
person("Angel", "Feliz", role="ctb"),
9494
person("Michael","Young", role="ctb"),
95-
person("Mark", "Seeto", role="ctb")
95+
person("Mark", "Seeto", role="ctb"),
96+
person("Philippe", "Grosjean", role="ctb"),
97+
person("Vincent", "Runge", role="ctb"),
98+
person("Christian", "Wia", role="ctb"),
99+
person("Elise", "Maigné", role="ctb"),
100+
person("Vincent", "Rocher", role="ctb"),
101+
person("Vijay", "Lulla", role="ctb")
96102
)

NEWS.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,11 @@ rowwiseDT(
105105
106106
10. `DT[1, on=NULL]` now works for returning the first row, [#6579](https://github.com/Rdatatable/data.table/issues/6579). Thanks to @Kodiologist for the report and @tdhock for the PR.
107107
108-
11. `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.
108+
11. `tables()` now returns the correct size for data.tables over 2GiB, [#6607](https://github.com/Rdatatable/data.table/issues/6607). Thanks to @vlulla for the report and the PR.
109+
110+
12. Joins on multiple columns, such as `x[y, on=c("x1==y1", "x2==y1")]`, could fail during implicit type coercions if `x1` and `x2` had different but still compatible types, [#6602](https://github.com/Rdatatable/data.table/issues/6602). This was particularly unexpected when columns `x1`, `x2`, and `y1` were all of the same class, e.g. `Date`, but differed in their underlying storage types. Thanks to Benjamin Schwendinger for the report and the fix.
111+
112+
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.
109113
110114
## NOTES
111115

R/bmerge.R

Lines changed: 90 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,25 @@
11

2+
3+
mergeType = function(x) {
4+
ans = typeof(x)
5+
if (ans=="integer") { if (is.factor(x)) ans = "factor" }
6+
else if (ans=="double") { if (inherits(x, "integer64")) ans = "integer64" }
7+
# do not call isReallyReal(x) yet because i) if both types are double we don't need to coerce even if one or both sides
8+
# are int-as-double, and ii) to save calling it until we really need it
9+
ans
10+
}
11+
12+
cast_with_atts = function(x, as.f) {
13+
ans = as.f(x)
14+
if (!is.null(attributes(x))) attributes(ans) = attributes(x)
15+
ans
16+
}
17+
18+
coerce_col = function(dt, col, from_type, to_type, from_name, to_name, verbose_msg=NULL) {
19+
if (!is.null(verbose_msg)) catf(verbose_msg, from_type, from_name, to_type, to_name, domain=NULL)
20+
set(dt, j=col, value=cast_with_atts(dt[[col]], match.fun(paste0("as.", to_type))))
21+
}
22+
223
bmerge = function(i, x, icols, xcols, roll, rollends, nomatch, mult, ops, verbose)
324
{
425
callersi = i
@@ -25,95 +46,110 @@ bmerge = function(i, x, icols, xcols, roll, rollends, nomatch, mult, ops, verbos
2546

2647
supported = c(ORDERING_TYPES, "factor", "integer64")
2748

28-
getClass = function(x) {
29-
ans = typeof(x)
30-
if (ans=="integer") { if (is.factor(x)) ans = "factor" }
31-
else if (ans=="double") { if (inherits(x, "integer64")) ans = "integer64" }
32-
# do not call isReallyReal(x) yet because i) if both types are double we don't need to coerce even if one or both sides
33-
# are int-as-double, and ii) to save calling it until we really need it
34-
ans
35-
}
36-
3749
if (nrow(i)) for (a in seq_along(icols)) {
3850
# - check that join columns have compatible types
3951
# - do type coercions if necessary on just the shallow local copies for the purpose of join
4052
# - handle factor columns appropriately
4153
# Note that if i is keyed, if this coerces i's key gets dropped by set()
42-
ic = icols[a]
43-
xc = xcols[a]
44-
xclass = getClass(x[[xc]])
45-
iclass = getClass(i[[ic]])
46-
xname = paste0("x.", names(x)[xc])
47-
iname = paste0("i.", names(i)[ic])
48-
if (!xclass %chin% supported) stopf("%s is type %s which is not supported by data.table join", xname, xclass)
49-
if (!iclass %chin% supported) stopf("%s is type %s which is not supported by data.table join", iname, iclass)
50-
if (xclass=="factor" || iclass=="factor") {
54+
icol = icols[a]
55+
xcol = xcols[a]
56+
x_merge_type = mergeType(x[[xcol]])
57+
i_merge_type = mergeType(i[[icol]])
58+
xname = paste0("x.", names(x)[xcol])
59+
iname = paste0("i.", names(i)[icol])
60+
if (!x_merge_type %chin% supported) stopf("%s is type %s which is not supported by data.table join", xname, x_merge_type)
61+
if (!i_merge_type %chin% supported) stopf("%s is type %s which is not supported by data.table join", iname, i_merge_type)
62+
if (x_merge_type=="factor" || i_merge_type=="factor") {
5163
if (roll!=0.0 && a==length(icols))
5264
stopf("Attempting roll join on factor column when joining %s to %s. Only integer, double or character columns may be roll joined.", xname, iname)
53-
if (xclass=="factor" && iclass=="factor") {
65+
if (x_merge_type=="factor" && i_merge_type=="factor") {
5466
if (verbose) catf("Matching %s factor levels to %s factor levels.\n", iname, xname)
55-
set(i, j=ic, value=chmatch(levels(i[[ic]]), levels(x[[xc]]), nomatch=0L)[i[[ic]]]) # nomatch=0L otherwise a level that is missing would match to NA values
67+
set(i, j=icol, value=chmatch(levels(i[[icol]]), levels(x[[xcol]]), nomatch=0L)[i[[icol]]]) # nomatch=0L otherwise a level that is missing would match to NA values
5668
next
5769
} else {
58-
if (xclass=="character") {
70+
if (x_merge_type=="character") {
5971
if (verbose) catf("Coercing factor column %s to type character to match type of %s.\n", iname, xname)
60-
set(i, j=ic, value=val<-as.character(i[[ic]]))
61-
set(callersi, j=ic, value=val) # factor in i joining to character in x will return character and not keep x's factor; e.g. for antaresRead #3581
72+
set(i, j=icol, value=val<-as.character(i[[icol]]))
73+
set(callersi, j=icol, value=val) # factor in i joining to character in x will return character and not keep x's factor; e.g. for antaresRead #3581
6274
next
63-
} else if (iclass=="character") {
75+
} else if (i_merge_type=="character") {
6476
if (verbose) catf("Matching character column %s to factor levels in %s.\n", iname, xname)
65-
newvalue = chmatch(i[[ic]], levels(x[[xc]]), nomatch=0L)
66-
if (anyNA(i[[ic]])) newvalue[is.na(i[[ic]])] = NA_integer_ # NA_character_ should match to NA in factor, #3809
67-
set(i, j=ic, value=newvalue)
77+
newvalue = chmatch(i[[icol]], levels(x[[xcol]]), nomatch=0L)
78+
if (anyNA(i[[icol]])) newvalue[is.na(i[[icol]])] = NA_integer_ # NA_character_ should match to NA in factor, #3809
79+
set(i, j=icol, value=newvalue)
6880
next
6981
}
7082
}
71-
stopf("Incompatible join types: %s (%s) and %s (%s). Factor columns must join to factor or character columns.", xname, xclass, iname, iclass)
83+
stopf("Incompatible join types: %s (%s) and %s (%s). Factor columns must join to factor or character columns.", xname, x_merge_type, iname, i_merge_type)
7284
}
73-
if (xclass == iclass) {
74-
if (verbose) catf("%s has same type (%s) as %s. No coercion needed.\n", iname, xclass, xname)
85+
# we check factors first to cater for the case when trying to do rolling joins on factors
86+
if (x_merge_type == i_merge_type) {
87+
if (verbose) catf("%s has same type (%s) as %s. No coercion needed.\n", iname, x_merge_type, xname)
7588
next
7689
}
77-
if (xclass=="character" || iclass=="character" ||
78-
xclass=="logical" || iclass=="logical" ||
79-
xclass=="factor" || iclass=="factor") {
80-
if (anyNA(i[[ic]]) && allNA(i[[ic]])) {
81-
if (verbose) catf("Coercing all-NA %s (%s) to type %s to match type of %s.\n", iname, iclass, xclass, xname)
82-
set(i, j=ic, value=match.fun(paste0("as.", xclass))(i[[ic]]))
90+
cfl = c("character", "logical", "factor")
91+
if (x_merge_type %chin% cfl || i_merge_type %chin% cfl) {
92+
msg = if(verbose) gettext("Coercing all-NA %s column %s to type %s to match type of %s.\n") else NULL
93+
if (anyNA(i[[icol]]) && allNA(i[[icol]])) {
94+
coerce_col(i, icol, i_merge_type, x_merge_type, iname, xname, msg)
8395
next
8496
}
85-
else if (anyNA(x[[xc]]) && allNA(x[[xc]])) {
86-
if (verbose) catf("Coercing all-NA %s (%s) to type %s to match type of %s.\n", xname, xclass, iclass, iname)
87-
set(x, j=xc, value=match.fun(paste0("as.", iclass))(x[[xc]]))
97+
if (anyNA(x[[xcol]]) && allNA(x[[xcol]])) {
98+
coerce_col(x, xcol, x_merge_type, i_merge_type, xname, iname, msg)
8899
next
89100
}
90-
stopf("Incompatible join types: %s (%s) and %s (%s)", xname, xclass, iname, iclass)
101+
stopf("Incompatible join types: %s (%s) and %s (%s)", xname, x_merge_type, iname, i_merge_type)
91102
}
92-
if (xclass=="integer64" || iclass=="integer64") {
103+
if (x_merge_type=="integer64" || i_merge_type=="integer64") {
93104
nm = c(iname, xname)
94-
if (xclass=="integer64") { w=i; wc=ic; wclass=iclass; } else { w=x; wc=xc; wclass=xclass; nm=rev(nm) } # w is which to coerce
105+
if (x_merge_type=="integer64") { w=i; wc=icol; wclass=i_merge_type; } else { w=x; wc=xcol; wclass=x_merge_type; nm=rev(nm) } # w is which to coerce
95106
if (wclass=="integer" || (wclass=="double" && !isReallyReal(w[[wc]]))) {
96107
if (verbose) catf("Coercing %s column %s%s to type integer64 to match type of %s.\n", wclass, nm[1L], if (wclass=="double") " (which contains no fractions)" else "", nm[2L])
97108
set(w, j=wc, value=bit64::as.integer64(w[[wc]]))
98109
} else stopf("Incompatible join types: %s is type integer64 but %s is type double and contains fractions", nm[2L], nm[1L])
99110
} else {
100111
# just integer and double left
101-
if (iclass=="double") {
102-
if (!isReallyReal(i[[ic]])) {
112+
ic_idx = which(icol == icols) # check if on is joined on multiple conditions, #6602
113+
if (i_merge_type=="double") {
114+
coerce_x = FALSE
115+
if (!isReallyReal(i[[icol]])) {
116+
coerce_x = TRUE
103117
# common case of ad hoc user-typed integers missing L postfix joining to correct integer keys
104118
# we've always coerced to int and returned int, for convenience.
105-
if (verbose) catf("Coercing double column %s (which contains no fractions) to type integer to match type of %s.\n", iname, xname)
106-
val = as.integer(i[[ic]])
107-
if (!is.null(attributes(i[[ic]]))) attributes(val) = attributes(i[[ic]]) # to retain Date for example; 3679
108-
set(i, j=ic, value=val)
109-
set(callersi, j=ic, value=val) # change the shallow copy of i up in [.data.table to reflect in the result, too.
110-
} else {
111-
if (verbose) catf("Coercing integer column %s to type double to match type of %s which contains fractions.\n", xname, iname)
112-
set(x, j=xc, value=as.double(x[[xc]]))
119+
if (length(ic_idx)>1L) {
120+
xc_idx = xcols[ic_idx]
121+
for (xb in xc_idx[which(vapply_1c(.shallow(x, xc_idx), mergeType) == "double")]) {
122+
if (isReallyReal(x[[xb]])) {
123+
coerce_x = FALSE
124+
break
125+
}
126+
}
127+
}
128+
if (coerce_x) {
129+
msg = if (verbose) gettext("Coercing %s column %s (which contains no fractions) to type %s to match type of %s.\n") else NULL
130+
coerce_col(i, icol, "double", "integer", iname, xname, msg)
131+
set(callersi, j=icol, value=i[[icol]]) # change the shallow copy of i up in [.data.table to reflect in the result, too.
132+
if (length(ic_idx)>1L) {
133+
xc_idx = xcols[ic_idx]
134+
for (xb in xc_idx[which(vapply_1c(.shallow(x, xc_idx), mergeType) == "double")]) {
135+
coerce_col(x, xb, "double", "integer", paste0("x.", names(x)[xb]), xname, msg)
136+
}
137+
}
138+
}
139+
}
140+
if (!coerce_x) {
141+
msg = if (verbose) gettext("Coercing %s column %s to type %s to match type of %s which contains fractions.\n") else NULL
142+
coerce_col(x, xcol, "integer", "double", xname, iname, msg)
113143
}
114144
} else {
115-
if (verbose) catf("Coercing integer column %s to type double for join to match type of %s.\n", iname, xname)
116-
set(i, j=ic, value=as.double(i[[ic]]))
145+
msg = if (verbose) gettext("Coercing %s column %s to type %s for join to match type of %s.\n") else NULL
146+
coerce_col(i, icol, "integer", "double", iname, xname, msg)
147+
if (length(ic_idx)>1L) {
148+
xc_idx = xcols[ic_idx]
149+
for (xb in xc_idx[which(vapply_1c(.shallow(x, xc_idx), mergeType) == "integer")]) {
150+
coerce_col(x, xb, "integer", "double", paste0("x.", names(x)[xb]), xname, msg)
151+
}
152+
}
117153
}
118154
}
119155
}

R/tables.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ type_size = function(DT) {
55
# for speed and ram efficiency, a lower bound by not descending into character string lengths or list items
66
# if a more accurate and higher estimate is needed then user can pass object.size or alternative to mb=
77
# in case number of columns is very large (e.g. 1e6 columns) then we use a for() to avoid allocation of sapply()
8-
ans = 0L
8+
ans = 0.0
99
lookup = c("raw"=1L, "integer"=4L, "double"=8L, "complex"=16L)
1010
for (i in seq_along(DT)) {
1111
col = DT[[i]]

inst/tests/other.Rraw

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,3 +766,10 @@ if (loaded[["nanotime"]]) {
766766
# respect dec=',' for nanotime, related to #6446, corresponding to tests 2281.*
767767
test(31, fwrite(data.table(as.nanotime(.POSIXct(0))), dec=',', sep=';'), output="1970-01-01T00:00:00,000000000Z")
768768
}
769+
770+
# tables() with large environment #6607
771+
.e <- new.env() ## to not populate the .GlobalEnv
772+
.e[["DT"]] <- as.data.table(lapply(1:15,function(i) runif(20e6)))
773+
res <- tables(env=.e)
774+
test(32, res[, .(NAME,NROW,NCOL,MB)], data.table(NAME="DT",NROW=20000000L,NCOL=15L,MB=2288.0))
775+
rm(.e, res)

0 commit comments

Comments
 (0)