Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rbind allow binding of different class attributes #5446

Merged
merged 39 commits into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
976c372
add fix #5309
ben-schwen Aug 25, 2022
3754191
fix test numbering
ben-schwen Aug 25, 2022
4077830
add rbind for ITime
ben-schwen Aug 25, 2022
f015eed
more tests
ben-schwen Aug 25, 2022
6ab44b0
add merge tests
ben-schwen Aug 25, 2022
65e7674
add AsIs #4934
ben-schwen Aug 26, 2022
2282d9f
add news
ben-schwen Sep 6, 2022
9527b89
news typo
ben-schwen Sep 6, 2022
a6d9c65
add ignore.attr argument
ben-schwen Dec 30, 2022
e2786a5
fix news
ben-schwen Dec 30, 2022
b9b0479
merge master
ben-schwen Dec 30, 2022
8c4d19c
change arguments of registered rbindlist
ben-schwen Dec 30, 2022
9d6afbc
add attribute to usage
ben-schwen Dec 30, 2022
5a4823c
move nanotime tests
ben-schwen Dec 30, 2022
452adc2
adjust test numbering
ben-schwen Dec 30, 2022
f967ce8
add test coverage
ben-schwen Jan 3, 2023
04ad6ee
prohibit NA for ignore.att
ben-schwen Dec 10, 2023
0949726
merge master
ben-schwen Dec 10, 2023
a1cc7a9
Merge branch 'master' into rbind_class_att
ben-schwen Mar 31, 2024
89f2541
move news
ben-schwen Mar 31, 2024
7353bc6
finish todo of #5857
ben-schwen Mar 31, 2024
8ad44ff
Merge branch 'master' into rbind_class_att
ben-schwen Jul 13, 2024
2e69fa1
Update NEWS.md
ben-schwen Jul 20, 2024
44b44f2
update comment
ben-schwen Jul 20, 2024
cc6479a
Merge branch 'rbind_class_att' of github.com:Rdatatable/data.table in…
ben-schwen Jul 20, 2024
677a47c
update doc for ignore.attr
ben-schwen Jul 20, 2024
3190651
fix nit ignoreattr
ben-schwen Jul 20, 2024
5757276
fix test consistency
ben-schwen Jul 20, 2024
d977e9b
remove setnames
ben-schwen Jul 20, 2024
62f9e9a
update asis test to use rbindlist
ben-schwen Jul 20, 2024
094bbfc
update test comments
ben-schwen Jul 20, 2024
66fa443
update NEWS num
ben-schwen Jul 20, 2024
dbdd16f
NEWS wording
MichaelChirico Jul 21, 2024
a4c73cc
more NEWS wording
MichaelChirico Jul 21, 2024
1f6ddf3
template message for i18n
MichaelChirico Jul 21, 2024
d77c1ea
simplify condition (C boolean --> no NA to worry about)
MichaelChirico Jul 21, 2024
45fa387
&& not &
MichaelChirico Jul 24, 2024
ceae807
Merge branch 'master' into rbind_class_att
MichaelChirico Jul 24, 2024
13ec07f
correct error message
MichaelChirico Jul 24, 2024
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
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,9 @@

41. `tables()` is faster by default by excluding the size of character strings in R's global cache (which may be shared) and excluding the size of list column items (which also may be shared). `mb=` now accepts any function which accepts a `data.table` and returns a higher and better estimate of its size in bytes, albeit more slowly; e.g. `mb = utils::object.size`.

43. `rbindlist(l, use.names=TRUE)` and `rbind` allows now the automatic binding of columns with different class attributes for certain classes such as `Date`, `IDate`, `ITime`, `POSIXct` and `AsIs` with other columns of similar classes, e.g., `IDate` and `Date`. The conversion is done automatically and the class attribute of the final column is determined by the first encountered class attribute in the binding list, [#5309](https://github.com/Rdatatable/data.table/issues/5309), [#4934](https://github.com/Rdatatable/data.table/issues/4934), [#5391](https://github.com/Rdatatable/data.table/issues/5391).
`rbindlist(l, ignore.attr=TRUE)` and `rbind` also gained argument `ignore.attr` to manually deactivate the safety-net of binding columns with different column classes, [#3911](https://github.com/Rdatatable/data.table/issues/3911), [#5542](https://github.com/Rdatatable/data.table/issues/5542). Thanks to @dcaseykc, @fox34, @adrian-quintario, @berg-michael, @arunsrinivasan, @statquant, @pkress, @jrausch12, @therosko, @OfekShilon, @iMissile, @tdhock for the request and @ben-schwen for the PR.

ben-schwen marked this conversation as resolved.
Show resolved Hide resolved
## BUG FIXES

1. `by=.EACHI` when `i` is keyed but `on=` different columns than `i`'s key could create an invalidly keyed result, [#4603](https://github.com/Rdatatable/data.table/issues/4603) [#4911](https://github.com/Rdatatable/data.table/issues/4911). Thanks to @myoung3 and @adamaltmejd for reporting, and @ColeMiller1 for the PR. An invalid key is where a `data.table` is marked as sorted by the key columns but the data is not sorted by those columns, leading to incorrect results from subsequent queries.
Expand Down
8 changes: 4 additions & 4 deletions R/data.table.R
Original file line number Diff line number Diff line change
Expand Up @@ -2740,14 +2740,14 @@ chgroup = function(x) {
}

# plain rbind and cbind methods are registered using S3method() in NAMESPACE only from R>=4.0.0; #3948
rbind.data.table = function(..., use.names=TRUE, fill=FALSE, idcol=NULL) {
rbind.data.table = function(..., use.names=TRUE, fill=FALSE, idcol=NULL, ignore.attr=FALSE) {
l = lapply(list(...), function(x) if (is.list(x)) x else as.data.table(x)) #1626; e.g. psych binds a data.frame|table with a matrix
rbindlist(l, use.names, fill, idcol)
rbindlist(l, use.names, fill, idcol, ignore.attr)
}
cbind.data.table = data.table
.rbind.data.table = rbind.data.table # the workaround using this in FAQ 2.24 is still applied to support R < 4.0.0

rbindlist = function(l, use.names="check", fill=FALSE, idcol=NULL) {
rbindlist = function(l, use.names="check", fill=FALSE, idcol=NULL, ignore.attr=FALSE) {
if (is.null(l)) return(null.data.table())
if (!is.list(l) || is.data.frame(l)) stopf("Input is %s but should be a plain list of items to be stacked", class(l)[1L])
if (isFALSE(idcol)) { idcol = NULL }
Expand All @@ -2763,7 +2763,7 @@ rbindlist = function(l, use.names="check", fill=FALSE, idcol=NULL) {
if (!miss) stopf("use.names='check' cannot be used explicitly because the value 'check' is new in v1.12.2 and subject to change. It is just meant to convey default behavior. See ?rbindlist.")
use.names = NA
}
ans = .Call(Crbindlist, l, use.names, fill, idcol)
ans = .Call(Crbindlist, l, use.names, fill, idcol, ignore.attr)
if (!length(ans)) return(null.data.table())
setDT(ans)[]
}
Expand Down
2 changes: 1 addition & 1 deletion R/merge.R
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ merge.data.table = function(x, y, by = NULL, by.x = NULL, by.y = NULL, all = FAL
# Perhaps not very commonly used, so not a huge deal that the join is redone here.
missingyidx = y[!x, which=TRUE, on=by, allow.cartesian=allow.cartesian]
if (length(missingyidx)) {
dt = rbind(dt, y[missingyidx], use.names=FALSE, fill=TRUE)
dt = rbind(dt, y[missingyidx], use.names=FALSE, fill=TRUE, ignore.attr=TRUE)
Copy link
Member

Choose a reason for hiding this comment

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

Do we have unit test that covers that? It looks like a breaking change, therefore should be introduced softly, having backward compatible default for 1-2 versions, before switching to new behavior.

Copy link
Member Author

Choose a reason for hiding this comment

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

We have now since #5857. I'm indifferent whether we add the change to merge or not. It makes the code much shorter and does not hide so much what we want to achieve but since many ppl rely on merge we should be careful about changes.

}
}
# X[Y] syntax puts JIS i columns at the end, merge likes them alongside i.
Expand Down
8 changes: 8 additions & 0 deletions inst/tests/other.Rraw
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,14 @@ if (loaded[["nanotime"]]) {
DT = data.table(time=nanotime(c(1,NA,3)))
test(27, na.omit(DT), DT[c(1,3)])

# was 2239 in tests.Rraw, rbind with vectors with class attributes #5309
ben-schwen marked this conversation as resolved.
Show resolved Hide resolved
x = data.table(a=1L, b=as.nanotime(0))
y = data.table(a=2L, b=NA)
test(27.01, rbind(x,y), data.table(a = c(1L, 2L), b=as.nanotime(c(0, NA))))
test(27.02, rbind(y,x), data.table(a = c(2L, 1L), b=as.nanotime(c(NA, 0))))
y[, b := NULL]
test(27.03, rbind(x,y, fill = TRUE), data.table(a = c(1L, 2L), b=as.nanotime(c(0, NA))))
test(27.04, rbind(y,x, fill = TRUE), data.table(a = c(2L, 1L), b=as.nanotime(c(NA, 0))))
}

# that plot works; moved from tests.Rraw 167 to here to save ram of loading graphics package and possible screen device issues on overloaded servers, #5517
Expand Down
45 changes: 45 additions & 0 deletions inst/tests/tests.Rraw
Original file line number Diff line number Diff line change
Expand Up @@ -14342,6 +14342,8 @@ test(2003.5, rbindlist(list(data.table(a=1:2), data.table(b=3:4, c=5:6)), fill=T
# rbindlist segfault with fill=TRUE and usenames=FALSE #5444
test(2003.6, rbindlist(list(list(1), list(2,3)), fill=TRUE, use.names=FALSE), data.table(c(1,2), c(NA, 3)))
test(2003.7, rbindlist(list(list(1), list(2,factor(3))), fill=TRUE, use.names=FALSE), data.table(c(1,2), factor(c(NA, 3))))
# rbindlist ignore attributes #3911
test(2003.8, rbindlist(list(), ignore.attr=1), error="ignore.attr= should be TRUE or FALSE")

# chmatch coverage for two different non-ascii encodings matching; issues mentioned in comments in chmatch.c #69 #2538 #111
x1 = "fa\xE7ile"
Expand Down Expand Up @@ -18111,3 +18113,46 @@ test(2238.9, NA %notin% c(1:5, NA), FALSE)

# shift actionable error on matrix input #5287
test(2239.1, shift(matrix(1:10, ncol = 1)), error="consider wrapping")

# rbind with vectors with class attributes #5309
x = data.table(a = 1L, b = as.Date("2020-01-01"))
y = data.table(a = 2L, b = as.IDate("2021-01-01"))
z = data.table(a = 3L, b = NA)
test(2240.01, rbind(x, y), data.table(a=1:2, b= as.Date(c("2020-01-01", "2021-01-01"))))
test(2240.02, rbind(y, x), data.table(a=c(2L, 1L), b=as.IDate(c("2021-01-01", "2020-01-01"))))
test(2240.03, rbind(x, z), data.table(a=c(1L, 3L), b= as.Date(c("2020-01-01", NA))))
test(2240.04, rbind(z, x), data.table(a=c(3L, 1L), b= as.Date(c(NA, "2020-01-01"))))
test(2240.05, rbind(y, z), data.table(a=c(2L, 3L), b=as.IDate(c("2021-01-01", NA))))
test(2240.06, rbind(z, y), data.table(a=c(3L, 2L), b=as.IDate(c(NA, "2021-01-01"))))
z[, b := NULL]
test(2240.07, rbind(x, z, fill=TRUE), data.table(a=c(1L, 3L), b= as.Date(c("2020-01-01", NA))))
test(2240.08, rbind(z, x, fill=TRUE), data.table(a=c(3L, 1L), b= as.Date(c(NA, "2020-01-01"))))
test(2240.09, rbind(y, z, fill=TRUE), data.table(a=c(2L, 3L), b=as.IDate(c("2021-01-01", NA))))
test(2240.10, rbind(z, y, fill=TRUE), data.table(a=c(3L, 2L), b=as.IDate(c(NA, "2021-01-01"))))
x = data.table(a=1L, b=as.POSIXct("2021-10-06 13:58:00 UTC"))
test(2240.11, rbind(x, z, fill=TRUE), data.table(a=c(1L, 3L), b=as.POSIXct(c("2021-10-06 13:58:00 UTC", NA))))
test(2240.12, rbind(z, x, fill=TRUE), data.table(a=c(3L, 1L), b=as.POSIXct(c(NA, "2021-10-06 13:58:00 UTC"))))
x = data.table(c=1L, d=as.POSIXct("2021-10-06 13:58:00 UTC"))
test(2240.13, rbind(x, z, fill=TRUE, use.names=FALSE), data.table(c = c(1L, 3L), d=as.POSIXct(c("2021-10-06 13:58:00 UTC", NA))))
test(2240.14, setnames(rbind(z, x, fill=TRUE, use.names=FALSE), c("c", "d")), data.table(c=c(3L, 1L), d=as.POSIXct(c(NA, "2021-10-06 13:58:00 UTC"))))
x = data.table(a=1L, b=as.ITime(0))
y = data.table(a=2L, b=NA)
test(2240.15, rbind(x,y), data.table(a = c(1L, 2L), b=as.ITime(c(0, NA))))
test(2240.16, rbind(y,x), data.table(a = c(2L, 1L), b=as.ITime(c(NA, 0))))
y[, b := NULL]
test(2240.17, rbind(x,y, fill = TRUE), data.table(a = c(1L, 2L), b=as.ITime(c(0, NA))))
test(2240.18, rbind(y,x, fill = TRUE), data.table(a = c(2L, 1L), b=as.ITime(c(NA, 0))))
x = data.table(a = 1L, b = as.Date("2020-01-01"))
y = data.table(a = 2L, b = NA)
test(2240.19, merge(x, y, by="a", all=TRUE), data.table(a=1:2, b.x=as.Date(c("2020-01-01", NA)), b.y=NA, key="a"))
test(2240.20, merge(y, x, by="a", all=TRUE), data.table(a=1:2, b.x=NA, key="a", b.y=as.Date(c("2020-01-01", NA))))
x = data.table(a = 1L, b=I(3L))
y = data.table(a = 2L, b=NA)
test(2240.21, rbind(x,y), data.table(a = c(1L, 2L), b=I(c(3L, NA))))
test(2240.22, rbind(y,x), data.table(a = c(2L, 1L), b=c(NA, 3L)))
# rbind ignore attributes #3911
x = data.table(a = structure(1:2, class=c("a", "integer")), key="a")
y = data.table(a = 2:3, key="a")
test(2240.31, merge(x,y, all.y=TRUE), data.table(a=structure(2:3, class=c("a", "integer")), key="a"))
test(2240.32, rbind(x,y), error="Class attribute .* does not match with .*")
test(2240.33, rbind(x,y, ignore.attr=TRUE), data.table(a=structure(c(1L, 2L, 2L, 3L), class=c("a", "integer"))))
8 changes: 7 additions & 1 deletion man/rbindlist.Rd
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@
Same as \code{do.call("rbind", l)} on \code{data.frame}s, but much faster.
}
\usage{
rbindlist(l, use.names="check", fill=FALSE, idcol=NULL)
rbindlist(l, use.names="check", fill=FALSE, idcol=NULL, ignore.attr=FALSE)
# rbind(..., use.names=TRUE, fill=FALSE, idcol=NULL)
}
\arguments{
\item{l}{ A list containing \code{data.table}, \code{data.frame} or \code{list} objects. \code{\dots} is the same but you pass the objects by name separately. }
\item{use.names}{\code{TRUE} binds by matching column name, \code{FALSE} by position. `check` (default) warns if all items don't have the same names in the same order and then currently proceeds as if `use.names=FALSE` for backwards compatibility (\code{TRUE} in future); see news for v1.12.2.}
\item{fill}{\code{TRUE} fills missing columns with NAs. By default \code{FALSE}.}
\item{idcol}{Creates a column in the result showing which list item those rows came from. \code{TRUE} names this column \code{".id"}. \code{idcol="file"} names this column \code{"file"}. If the input list has names, those names are the values placed in this id column, otherwise the values are an integer vector \code{1:length(l)}. See \code{examples}.}
\item{ignore.attr}{\code{TRUE} allows binding columns with different class attributes. By default \code{FALSE}.}
ben-schwen marked this conversation as resolved.
Show resolved Hide resolved
}
\details{
Each item of \code{l} can be a \code{data.table}, \code{data.frame} or \code{list}, including \code{NULL} (skipped) or an empty object (0 rows). \code{rbindlist} is most useful when there are an unknown number of (potentially many) objects to stack, such as returned by \code{lapply(fileNames, fread)}. \code{rbind} is most useful to stack two or three objects which you know in advance. \code{\dots} should contain at least one \code{data.table} for \code{rbind(\dots)} to call the fast method and return a \code{data.table}, whereas \code{rbindlist(l)} always returns a \code{data.table} even when stacking a plain \code{list} with a \code{data.frame}, for example.
Expand Down Expand Up @@ -54,6 +55,11 @@ rbindlist(l, use.names=TRUE, fill=TRUE, idcol=TRUE)
setattr(l, 'names', c("a", "b"))
rbindlist(l, use.names=TRUE, fill=TRUE, idcol="ID")

# bind different classes
DT1 = data.table(A=1:3,B=letters[1:3])
DT2 = data.table(A=4:5,B=letters[4:5])
setattr(DT1[["A"]], "class", c("a", "integer"))
rbind(DT1, DT2, ignore.attr=TRUE)
}
\keyword{ data }

3 changes: 2 additions & 1 deletion src/data.table.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ extern SEXP char_datatable;
extern SEXP char_dataframe;
extern SEXP char_NULL;
extern SEXP char_maxString;
extern SEXP char_AsIs;
extern SEXP sym_sorted;
extern SEXP sym_index;
extern SEXP sym_BY;
Expand Down Expand Up @@ -282,7 +283,7 @@ SEXP chmatchdup_R(SEXP, SEXP, SEXP);
SEXP chin_R(SEXP, SEXP);
SEXP freadR(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP);
SEXP fwriteR(SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP, SEXP);
SEXP rbindlist(SEXP, SEXP, SEXP, SEXP);
SEXP rbindlist(SEXP, SEXP, SEXP, SEXP, SEXP);
SEXP setlistelt(SEXP, SEXP, SEXP);
SEXP address(SEXP);
SEXP expandAltRep(SEXP);
Expand Down
2 changes: 2 additions & 0 deletions src/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ SEXP char_datatable;
SEXP char_dataframe;
SEXP char_NULL;
SEXP char_maxString;
SEXP char_AsIs;
SEXP sym_sorted;
SEXP sym_index;
SEXP sym_BY;
Expand Down Expand Up @@ -258,6 +259,7 @@ void attribute_visible R_init_data_table(DllInfo *info)
char_dataframe = PRINTNAME(install("data.frame"));
char_NULL = PRINTNAME(install("NULL"));
char_maxString = PRINTNAME(install("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"));
char_AsIs = PRINTNAME(install("AsIs"));

if (TYPEOF(char_integer64) != CHARSXP) {
// checking one is enough in case of any R-devel changes
Expand Down
25 changes: 20 additions & 5 deletions src/rbindlist.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@
#include <Rdefines.h>
#include <ctype.h> // for isdigit

SEXP rbindlist(SEXP l, SEXP usenamesArg, SEXP fillArg, SEXP idcolArg)
SEXP rbindlist(SEXP l, SEXP usenamesArg, SEXP fillArg, SEXP idcolArg, SEXP ignoreattrArg)
{
if (!isLogical(fillArg) || LENGTH(fillArg) != 1 || LOGICAL(fillArg)[0] == NA_LOGICAL)
error(_("fill= should be TRUE or FALSE"));
if (!isLogical(usenamesArg) || LENGTH(usenamesArg)!=1)
error(_("use.names= should be TRUE, FALSE, or not used (\"check\" by default)")); // R levels converts "check" to NA
if (!isLogical(ignoreattrArg) || LENGTH(ignoreattrArg)!=1 || LOGICAL(ignoreattrArg)[0] == NA_LOGICAL)
error(_("ignore.attr= should be TRUE or FALSE"));
MichaelChirico marked this conversation as resolved.
Show resolved Hide resolved
if (!length(l)) return(l);
if (TYPEOF(l) != VECSXP) error(_("Input to rbindlist must be a list. This list can contain data.tables, data.frames or plain lists."));
Rboolean usenames = LOGICAL(usenamesArg)[0];
const bool fill = LOGICAL(fillArg)[0];
const bool ignoreatt = LOGICAL(ignoreattrArg)[0];
ben-schwen marked this conversation as resolved.
Show resolved Hide resolved
if (fill && usenames==NA_LOGICAL) {
usenames=TRUE;
}
Expand Down Expand Up @@ -274,7 +277,7 @@ SEXP rbindlist(SEXP l, SEXP usenamesArg, SEXP fillArg, SEXP idcolArg)
bool factor=false, orderedFactor=false; // ordered factor is class c("ordered","factor"). isFactor() is true when isOrdered() is true.
int longestLen=-1, longestW=-1, longestI=-1; // just for ordered factor; longestLen must be initialized as -1 so that rbind zero-length ordered factor could work #4795
SEXP longestLevels=R_NilValue; // just for ordered factor
bool int64=false;
bool int64=false, date=false, posixct=false, itime=false, asis=false;
const char *foundName=NULL;
bool anyNotStringOrFactor=false;
SEXP firstCol=R_NilValue;
Expand Down Expand Up @@ -305,14 +308,25 @@ SEXP rbindlist(SEXP l, SEXP usenamesArg, SEXP fillArg, SEXP idcolArg)
if (INHERITS(thisCol, char_integer64)) {
if (firsti>=0 && !length(getAttrib(firstCol, R_ClassSymbol))) { firsti=i; firstw=w; firstCol=thisCol; } // so the integer64 attribute gets copied to target below
int64=true;
} else if (INHERITS(thisCol, char_Date)) {
if (firsti>=0 && !length(getAttrib(firstCol, R_ClassSymbol))) { firsti=i; firstw=w; firstCol=thisCol; }
MichaelChirico marked this conversation as resolved.
Show resolved Hide resolved
date=true;
} else if (INHERITS(thisCol, char_POSIXct)) {
if (firsti>=0 && !length(getAttrib(firstCol, R_ClassSymbol))) { firsti=i; firstw=w; firstCol=thisCol; }
posixct=true;
} else if (INHERITS(thisCol, char_ITime)) {
if (firsti>=0 && !length(getAttrib(firstCol, R_ClassSymbol))) { firsti=i; firstw=w; firstCol=thisCol; }
itime=true;
} else if (!asis && INHERITS(thisCol, char_AsIs)) {
asis=true;
}
if (firsti==-1) { firsti=i; firstw=w; firstCol=thisCol; }
else {
if (!factor && !int64) {
if (!factor && !int64 && ((!date && !posixct) || (date && posixct)) && !itime &!asis) { // prohibit binding of date and posixct
MichaelChirico marked this conversation as resolved.
Show resolved Hide resolved
if (!R_compute_identical(PROTECT(getAttrib(thisCol, R_ClassSymbol)),
PROTECT(getAttrib(firstCol, R_ClassSymbol)),
0)) {
error(_("Class attribute on column %d of item %d does not match with column %d of item %d."), w+1, i+1, firstw+1, firsti+1);
0) && !ignoreatt) {
error(_("Class attribute on column %d of item %d does not match with column %d of item %d. You can deactivate this safety-check by using ignore.attr=TRUE"), w+1, i+1, firstw+1, firsti+1);
}
UNPROTECT(2);
}
Expand All @@ -323,6 +337,7 @@ SEXP rbindlist(SEXP l, SEXP usenamesArg, SEXP fillArg, SEXP idcolArg)
if (factor) maxType=INTSXP; // if any items are factors then a factor is created (could be an option)
if (int64 && maxType!=REALSXP)
error(_("Internal error: column %d of result is determined to be integer64 but maxType=='%s' != REALSXP"), j+1, type2char(maxType)); // # nocov
if (date && INHERITS(firstCol, char_IDate)) maxType=INTSXP; // first encountered Date determines class and type #5309
SEXP target;
SET_VECTOR_ELT(ans, idcol+j, target=allocVector(maxType, nrow)); // does not initialize logical & numerics, but does initialize character and list
if (!factor) copyMostAttrib(firstCol, target); // all but names,dim and dimnames; mainly for class. And if so, we want a copy here, not keepattr's SET_ATTRIB.
Expand Down
Loading