Problem:
What is the data.table
equivalent of tidyr
's complete
command with group by
?
What is the relationship between on
and by
for data.table
?
Example:
dt=data.table(a = c(1,1,2,2,3,3,4,4) , b = c(4,5,6,7,8,9,10,11) , c = c("x","x","x","x","y","y","y","y"))
show(dt)
a b c
1: 1 4 x
2: 1 5 x
3: 2 6 x
4: 2 7 x
5: 3 8 y
6: 3 9 y
7: 4 10 y
8: 4 11 y
The goal is to obtain the following:
a b c
1 4 x
1 5 x
1 6 x
1 7 x
2 4 x
2 5 x
2 6 x
2 7 x
3 8 y
3 9 y
3 10 y
3 11 y
4 8 y
4 9 y
4 10 y
4 11 y
so something approximately like this:
setDT(dt)[CJ(a=a,b=b,unique=TRUE), on=.(a,b) , by = .(c)]
but it doesn't work and the data.table
documentation is thin on this aspect of the syntax.
Insufficient solutions:
The following SO posts address similar problems, but do not provide sufficient solutions in this context.
by
)by
)by
command)Try this:
dt[, CJ(a = a, b = b, unique = TRUE), by = "c"]
giving:
c a b
1: x 1 4
2: x 1 5
3: x 1 6
4: x 1 7
5: x 2 4
6: x 2 5
7: x 2 6
8: x 2 7
9: y 3 8
10: y 3 9
11: y 3 10
12: y 3 11
13: y 4 8
14: y 4 9
15: y 4 10
16: y 4 11
complete
retains other unrelated columns, so I'll add one...
library(data.table)
dt = data.table(
a = c(1,1,2,2,3,3,4,4) ,
b = c(4,5,6,7,8,9,10,11) ,
c = c("x","x","x","x","y","y","y","y"),
d = LETTERS[10 + 1:8])
a b c d
1: 1 4 x K
2: 1 5 x L
3: 2 6 x M
4: 2 7 x N
5: 3 8 y O
6: 3 9 y P
7: 4 10 y Q
8: 4 11 y R
To complete the a x b combos for each c, I would make a new table with those combos (exactly as already in @G.Grothendieck's answer) and update-join to get d and other non-combo columns:
mDT = dt[, CJ(a = a, b = b, unique=TRUE), by=c]
cvars = copy(names(mDT))
ovars = setdiff(names(dt), cvars)
mDT[, (ovars) := dt[.SD, on=cvars, mget(sprintf("x.%s", ovars))]]
setcolorder(mDT, names(dt))
a b c d
1: 1 4 x K
2: 1 5 x L
3: 1 6 x <NA>
4: 1 7 x <NA>
5: 2 4 x <NA>
6: 2 5 x <NA>
7: 2 6 x M
8: 2 7 x N
9: 3 8 y O
10: 3 9 y P
11: 3 10 y <NA>
12: 3 11 y <NA>
13: 4 8 y <NA>
14: 4 9 y <NA>
15: 4 10 y Q
16: 4 11 y R
Alternately, you could do an inner (?) join, though this is inefficient since it creates two new tables:
dt[mDT, on=cvars]
# or more concisely....
dt[dt[, CJ(a = a, b = b, unique=TRUE), by=c], on=.(a,b,c)]
Or, do one inner join per by=
group (from @eddi):
dt[, .SD[CJ(a = a, b = b, unique = TRUE), on = .(a, b)], by = c]
For comparison in the tidyverse:
library(dplyr); library(tidyr)
data.frame(dt) %>% group_by(c) %>% complete(a, b)
# A tibble: 16 x 4
# Groups: c [2]
c a b d
<chr> <dbl> <dbl> <chr>
1 x 1 4 K
2 x 1 5 L
3 x 1 6 <NA>
4 x 1 7 <NA>
5 x 2 4 <NA>
6 x 2 5 <NA>
7 x 2 6 M
8 x 2 7 N
9 y 3 8 O
10 y 3 9 P
11 y 3 10 <NA>
12 y 3 11 <NA>
13 y 4 8 <NA>
14 y 4 9 <NA>
15 y 4 10 Q
16 y 4 11 R
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With