diff --git a/DESCRIPTION b/DESCRIPTION index b8439044c9..69e1eb9147 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -68,7 +68,8 @@ Authors@R: c( person("Hadley","Wickham", role="ctb"), person("Bennet","Becker", role="ctb"), person("Kyle","Haynes", role="ctb"), - person("Boniface Christian","Kamgang", role="ctb")) + person("Boniface Christian","Kamgang", role="ctb"), + person("Odel","Marcelle", role="ctb")) Depends: R (>= 3.1.0) Imports: methods Suggests: bit64 (>= 4.0.0), bit (>= 4.0.4), curl, R.utils, xts, nanotime, zoo (>= 1.8-1), yaml, knitr, rmarkdown, markdown diff --git a/NAMESPACE b/NAMESPACE index 999a834304..00cb51a4cb 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -121,6 +121,7 @@ S3method(split, data.table) export(dcast, melt) S3method(dcast, data.table) S3method(melt, data.table) +S3method(melt, default) # exported for historical reasons -- if reshape2 is higher on search path, # dcast(DT) will not dispatch since reshape2::dcast is not generic. So users diff --git a/NEWS.md b/NEWS.md index 1836a4b3b4..48192c232b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -107,6 +107,8 @@ 20. `dcast()` now supports complex values in `value.var`, [#4855](https://github.com/Rdatatable/data.table/issues/4855). This extends earlier support for complex values in `formula`. Thanks Elio Campitelli for the request, and Michael Chirico for the PR. +21. `melt()` was pseudo generic in that `melt(DT)` would dispatch to the `melt.data.table` method but `melt(not-DT)` would explicitly redirect to `reshape2`. Now `melt()` is standard generic so that methods can be developed in other packages, [#4864](https://github.com/Rdatatable/data.table/pull/4864). Thanks to @odelmarcelle for suggesting and implementing. + ## 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. diff --git a/R/fmelt.R b/R/fmelt.R index 5038894ba0..243480445b 100644 --- a/R/fmelt.R +++ b/R/fmelt.R @@ -4,18 +4,18 @@ # redirection as well melt = function(data, ..., na.rm = FALSE, value.name = "value") { - if (is.data.table(data)) { - UseMethod("melt", data) - # if data is not data.table and reshape2 is installed, this won't dispatch to reshape2's method; - # CRAN package edarf and others fail without the else branch + UseMethod("melt", data) +} + +melt.default = function(data, ..., na.rm = FALSE, value.name = "value") { + # if no registered method exists for data, attempts to redirect data to reshape2::melt; + # CRAN package edarf and others fail without the redirection # nocov start - } else { - data_name = deparse(substitute(data)) - ns = tryCatch(getNamespace("reshape2"), error=function(e) - stopf("The %1$s generic in data.table has been passed a %2$s, but data.table::%1$s currently only has a method for data.tables. Please confirm your input is a data.table, with setDT(%3$s) or as.data.table(%3$s). If you intend to use a method from reshape2, try installing that package first, but do note that reshape2 is superseded and is no longer actively developed.", "melt", class(data)[1L], data_name)) - warningf("The %1$s generic in data.table has been passed a %2$s and will attempt to redirect to the relevant reshape2 method; please note that reshape2 is superseded and is no longer actively developed, and this redirection is now deprecated. Please do this redirection yourself like reshape2::%1$s(%3$s). In the next version, this warning will become an error.", "melt", class(data)[1L], data_name) - ns$melt(data, ..., na.rm=na.rm, value.name=value.name) - } + data_name = deparse(substitute(data)) + ns = tryCatch(getNamespace("reshape2"), error=function(e) + stopf("The %1$s generic in data.table has been passed a %2$s, but data.table::%1$s currently only has a method for data.tables. Please confirm your input is a data.table, with setDT(%3$s) or as.data.table(%3$s). If you intend to use a method from reshape2, try installing that package first, but do note that reshape2 is superseded and is no longer actively developed.", "melt", class(data)[1L], data_name)) + warningf("The %1$s generic in data.table has been passed a %2$s and will attempt to redirect to the relevant reshape2 method; please note that reshape2 is superseded and is no longer actively developed, and this redirection is now deprecated. To continue using melt methods from reshape2 while both libraries are attached, e.g. melt.list, you can prepend the namespace, i.e. reshape2::%1$s(%3$s). In the next version, this warning will become an error.", "melt", class(data)[1L], data_name) + ns$melt(data, ..., na.rm=na.rm, value.name=value.name) # nocov end } diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index 2d5df0498d..fa2346dd7b 100644 --- a/inst/tests/tests.Rraw +++ b/inst/tests/tests.Rraw @@ -3256,6 +3256,15 @@ Sep,33.5,19.4,15.7,11.9,0,100.8,100.8,0,12.7,12.7,0,174.1") x[ , r := as.raw(c(0, 1))] test(1037.414, melt(x, id.vars='x1', measure.vars='r'), error="Unknown column type 'raw' for column 'r'") + + # test dispatch for non-data.table objects. See #4864. Only possible to test the error message on CI. + test(1038.001, melt(as.data.frame(DT), id.vars=1:2, measure.vars=5:6), + error="The melt generic in data.table has been passed a data.frame") + + # uncomment 1038.002 and comment 1308.001 if reshape2 is ever available on CI. + # test(1038.002, melt(as.data.frame(DT), id.vars=1:2, measure.vars=5:6), as.data.frame(melt(DT, id.vars=1:2, measure.vars=5:6)), + # warning="The melt generic in data.table has been passed a data.frame") + } # sorting and grouping of Inf, -Inf, NA and NaN, #117, #112 & #105