使用data.table的group_by,on和by语法实现类似于tidyr::complete的功能。

4

问题:

data.table 中,如何使用 group by 实现与 tidyrcomplete 命令相同的效果?

onbydata.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"))
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

目标是获得以下内容:
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

所以大概是这样:

setDT(dt)[CJ(a=a,b=b,unique=TRUE), on=.(a,b) , by = .(c)]

但是它不起作用,而且 data.table 的文档在语法的这个方面上很薄弱。

不充分的解决方案:

以下 SO 帖子解决了类似的问题,但在这种情况下并没有提供充分的解决方案。

3个回答

5

试试这个:

dt[, CJ(a = a, b = b, unique = TRUE), by = "c"]

提供:

    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

为什么不再需要使用 on - cmo
1
这里没有合并,但是on用于执行合并。 - G. Grothendieck
在真实数据集中,我丢失了所有额外的列。这是我的错,我会更新示例。 - cmo
3
在这种情况下,请之后进行合并操作。 - G. Grothendieck

5

complete 保留了其他无关的列,所以我会添加一个...

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

为了完成每个 c 的 a x b 组合,我会创建一个新表格来包含这些组合(与 @G.Grothendieck 的答案完全相同),并使用更新连接获取 d 和其他非组合列。
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

或者,您可以进行内部连接​​,但这种方法效率低下,因为它会创建两个新表:

dt[mDT, on=cvars]

# or more concisely....

dt[dt[, CJ(a = a, b = b, unique=TRUE), by=c], on=.(a,b,c)]

或者,针对每个 by= 分组执行一个内部连接(来自@eddi):
dt[, .SD[CJ(a = a, b = b, unique = TRUE), on = .(a, b)], by = c]

在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    

2
如果c值不太多,我会执行dt[, .SD[CJ(a = a, b = b, unique = TRUE), on = .(a, b)], by = c] - eddi

1
以下内容将返回所需结果。
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"))
dttrue <- fread('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')
dt2 <- dt[,CJ(a=a,b=b,unique = TRUE),by = c]
all.equal(dt2[,.(a,b,c)], dttrue) #true

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接