Skip to content

Commit 1bd88cb

Browse files
Allow passing data.frame directly to dcast(), melt() (#7634)
* feat(7614): added s3 method at dcast and melt for data.frame * feat(7614): added to news * feat(7614): code styling * feat(7614): bug fix on dcast * feat(7614): added s3method to namespace * feat(7614): fix test 2365.1 as its calling undefined columns * feat(7614): removed s3method and used redirection incase of data.frame for dcast/melt * feat(7614): code linting * feat(7614): use call to capture args and redirect to dcast.data.table and melt.data.table * feat(7614): updated tests and dcast, melt * Tidy up NEWS * sync deprecated test comments --------- Co-authored-by: Michael Chirico <chiricom@google.com>
1 parent afe1218 commit 1bd88cb

File tree

4 files changed

+24
-8
lines changed

4 files changed

+24
-8
lines changed

NEWS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
- Type conversion support in GForce expressions (e.g., `sum(as.numeric(x))` will use GForce, saving the need to coerce `x` in a setup step) [#2934](https://github.com/Rdatatable/data.table/issues/2934)
2727
- Arithmetic operation support in GForce (e.g., `max(x) - min(x)` will use GForce on both `max(x)` and `min(x)`, saving the need to do the subtraction in a follow-up step) [#3815](https://github.com/Rdatatable/data.table/issues/3815)
2828

29+
4. `dcast()` and `melt()` "just work" when passed a data.frame, not just data.tables, with no need for coercion, [#7614](https://github.com/Rdatatable/data.table/issues/7614). Thanks @MichaelChirico for the suggestion and @manmita for the PR. Note that to avoid potential conflicts with {reshape2}'s data.frame methods, we do the dispatch to the data.table method manually.
30+
2931
### BUG FIXES
3032

3133
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.

R/fcast.R

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ dcast = function(
1212
data, formula, fun.aggregate = NULL, ..., margins = NULL,
1313
subset = NULL, fill = NULL, value.var = guess(data)
1414
) {
15+
if (!is.data.table(data) && is.data.frame(data)){
16+
mc <- match.call()
17+
mc[[1L]] <- as.name("dcast.data.table")
18+
return(eval(mc, parent.frame()))
19+
}
1520
UseMethod("dcast", data)
1621
}
1722

@@ -119,7 +124,6 @@ aggregate_funs = function(funs, vals, sep="_", ...) {
119124
}
120125

121126
dcast.data.table = function(data, formula, fun.aggregate = NULL, sep = "_", ..., margins = NULL, subset = NULL, fill = NULL, drop = TRUE, value.var = guess(data), verbose = getOption("datatable.verbose"), value.var.in.dots = FALSE, value.var.in.LHSdots = value.var.in.dots, value.var.in.RHSdots = value.var.in.dots) {
122-
if (!is.data.table(data)) stopf("'%s' must be a data.table", "data")
123127
drop = as.logical(rep_len(drop, 2L))
124128
if (anyNA(drop)) stopf("'drop' must be logical vector with no missing entries")
125129
if (!isTRUEorFALSE(value.var.in.dots))

R/fmelt.R

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44
# redirection as well
55

66
melt = function(data, ..., na.rm = FALSE, value.name = "value") {
7+
if (!is.data.table(data) && is.data.frame(data)){
8+
mc <- match.call()
9+
mc[[1L]] <- as.name("melt.data.table")
10+
return(eval(mc, parent.frame()))
11+
}
712
UseMethod("melt", data)
813
}
914

@@ -176,7 +181,6 @@ measurev = function(fun.list, sep="_", pattern, cols, multiple.keyword="value.na
176181
melt.data.table = function(data, id.vars, measure.vars, variable.name = "variable",
177182
value.name = "value", ..., na.rm = FALSE, variable.factor = TRUE, value.factor = FALSE,
178183
verbose = getOption("datatable.verbose")) {
179-
if (!is.data.table(data)) stopf("'%s' must be a data.table", "data")
180184
for(type.vars in c("id.vars","measure.vars")){
181185
sub.lang <- substitute({
182186
if (missing(VAR)) VAR=NULL

inst/tests/tests.Rraw

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13042,9 +13042,7 @@ test(1953.2, melt(DT, id.vars = 'id', measure.vars = patterns(a = 'a', b = 'b',
1304213042
error = 'Patterns not found')
1304313043
test(1953.3, melt(DT, id.vars = 'id', measure.vars = patterns(1L)),
1304413044
error = 'Input patterns must be of type character')
13045-
setDF(DT)
13046-
test(1953.4, melt.data.table(DT, id.vars = 'id', measure.vars = 'a'),
13047-
error = "must be a data.table")
13045+
# 1953.4 was a test of melt() erroring when passed a data.frame, no longer the case, #7614
1304813046

1304913047
# appearance order of two low-cardinality columns that were squashed in pr#3124
1305013048
DT = data.table(A=INT(1,3,2,3,2), B=1:5) # respect groups in 1st column (3's and 2's)
@@ -13389,9 +13387,7 @@ test(1962.083, guess(DT), '(all)')
1338913387
setnames(DT, 'V')
1339013388
test(1962.084, guess(DT), 'V',
1339113389
message = 'Using.*value column.*override')
13392-
setDF(DT)
13393-
test(1962.085, dcast.data.table(DT), error = 'must be a data.table')
13394-
setDT(DT)
13390+
# 1962.085 was a test of dcast() erroring when passed a data.frame, no longer the case, #7614
1339513391
test(1962.086, dcast(DT, a ~ a, drop = NA),
1339613392
error = "'drop' must be logical vector with no missing entries")
1339713393
DT = data.table(a = c(1, 1, 2, 2), b = list(1, 2, 3, 4), c = c(4, 4, 2, 2))
@@ -21509,3 +21505,13 @@ setdroplevels(x)
2150921505
setdroplevels(y)
2151021506
test(2364.2, levels(x$a), levels(y$a))
2151121507
rm(x, y)
21508+
21509+
# test for data.frame reshape for melt
21510+
df_melt = data.frame(a = 1:2, b = 3:4)
21511+
dt_melt = data.table(a = 1:2, b = 3:4)
21512+
test(2365.1, melt(df_melt, id.vars=1:2), melt(dt_melt, id.vars=1:2))
21513+
21514+
# test for data.frame reshape for dcast
21515+
df_dcast = data.frame(a = c("x", "y"), b = 1:2, v = 3:4)
21516+
dt_dcast = data.table(a = c("x", "y"), b = 1:2, v = 3:4)
21517+
test(2365.2, dcast(df_dcast, a ~ b, value.var = "v"), dcast(dt_dcast, a ~ b, value.var = "v"))

0 commit comments

Comments
 (0)