diff --git a/NEWS.md b/NEWS.md index bcaf69f92c..dc8376cb74 100644 --- a/NEWS.md +++ b/NEWS.md @@ -154,6 +154,8 @@ 2. `fintersect()` now retains the order of the first argument as reasonably expected, rather than retaining the order of the second argument, [#4716](https://github.com/Rdatatable/data.table/issues/4716). Thanks to Michel Lang for reporting, and Ben Schwen for the PR. +3. `setDT()` no longer modifies the class of other names bound to the origin data.frame - e.g., argument DFs names at a caller to a function which uses setDT. Cf [#4784](https://github.com/Rdatatable/data.table/issues/4784). Thanks @OfekShilon for the report and fix. + ## NOTES 1. Compiling from source no longer requires `zlib` header files to be available, [#4844](https://github.com/Rdatatable/data.table/pull/4844). The output suggests installing `zlib` headers, and how (e.g. `zlib1g-dev` on Ubuntu) as before, but now proceeds with `gzip` compression disabled in `fwrite`. Upon calling `fwrite(DT, "file.csv.gz")` at runtime, an error message suggests to reinstall `data.table` with `zlib` headers available. This does not apply to users on Windows or Mac who install the pre-compiled binary package from CRAN. diff --git a/R/data.table.R b/R/data.table.R index 0d51beafff..696e33faaf 100644 --- a/R/data.table.R +++ b/R/data.table.R @@ -2755,9 +2755,11 @@ setDT = function(x, keep.rownames=FALSE, key=NULL, check.names=FALSE) { rn = if (!identical(keep.rownames, FALSE)) rownames(x) else NULL setattr(x, "row.names", .set_row_names(nrow(x))) if (check.names) setattr(x, "names", make.names(names(x), unique=TRUE)) - # fix for #1078 and #1128, see .resetclass() for explanation. - setattr(x, "class", .resetclass(x, 'data.frame')) + # setalloccol results in a shallow copy. Must be performed before class setting, + # to have the class apply only to the new copy. #4784 setalloccol(x) + # fix for #1078 and #1128, see .resetclass() for explanation. + setattr(x,"class",c("data.table","data.frame")) if (!is.null(rn)) { nm = c(if (is.character(keep.rownames)) keep.rownames[1L] else "rn", names(x)) x[, (nm[1L]) := rn] @@ -2796,8 +2798,10 @@ setDT = function(x, keep.rownames=FALSE, key=NULL, check.names=FALSE) { if (check.names) setattr(x, "names", make.names(xn, unique=TRUE)) } setattr(x,"row.names",.set_row_names(n_range[2L])) - setattr(x,"class",c("data.table","data.frame")) + # setalloccol results in a shallow copy. Must be performed before class setting, + # to have the class apply only to the new copy. #4784 setalloccol(x) + setattr(x,"class",c("data.table","data.frame")) } else { stop("Argument 'x' to 'setDT' should be a 'list', 'data.frame' or 'data.table'") } diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index f17961e760..b5577949bb 100644 --- a/inst/tests/tests.Rraw +++ b/inst/tests/tests.Rraw @@ -17537,3 +17537,9 @@ vr = "Species" IDT[, virginca := get(vr) == "virginica"] ans = data.table(round = c(3, 3, 3, 2, 2, 4, 2, 4), k = c(6, 7, 8, 5, 7, 7, 6, 8), kar = structure(c(3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L), .Label = c("setosa", "versicolor", "virginica"), class = "factor"), N = c(24L, 14L, 4L, 1L, 1L, 1L, 3L, 2L)) test(2184.5, IDT[(virginca), .N, by = .(round(Sepal.Width), k = round(Sepal.Length), kar = get(vr))] , ans) + +# setDT no longer leaks class modification to origin copy, #4784 +d1 <- data.frame(a=1) +d2 <- d1 +setDT(d2) +test(2185, !is.data.table(d1)) \ No newline at end of file diff --git a/src/assign.c b/src/assign.c index bcd7f29265..7739f8d67c 100644 --- a/src/assign.c +++ b/src/assign.c @@ -208,8 +208,6 @@ SEXP alloccol(SEXP dt, R_len_t n, Rboolean verbose) R_len_t l, tl; if (isNull(dt)) error(_("alloccol has been passed a NULL dt")); if (TYPEOF(dt) != VECSXP) error(_("dt passed to alloccol isn't type VECSXP")); - klass = getAttrib(dt, R_ClassSymbol); - if (isNull(klass)) error(_("dt passed to alloccol has no class attribute. Please report result of traceback() to data.table issue tracker.")); l = LENGTH(dt); names = getAttrib(dt,R_NamesSymbol); // names may be NULL when null.data.table() passes list() to alloccol for example.