R data.table:通过分组从第二个表中随机抽取行样本

3
假设我们有两个表格,每个表格都有一个在两个表格之间共同的分组指示器。然后对于DT1中的每一行,我们希望在每个组内从DT2中随机选择2行。
解决这个问题的一种可能的方法是在DT2中随机生成一个整数列,使其与DT1的行相对应,并将两个表格连接起来。但是每个组的行数不同,我们不清楚如何按组长度进行条件处理。
最小工作示例:
DT1 <- data.table(var1=seq(1:20),
                  group=c(1,1,1,1,1,2,2,2,2,3,3,3,4,4,4,4,4,4,4,4))
DT2 <- data.table(obs=seq(1:13),
                  group=c(1,1,1,2,2,2,3,3,3,4,4,4,5))

视图:

DT1
    var1 group
 1:    1     1
 2:    2     1
 3:    3     1
 4:    4     1
 5:    5     1
 6:    6     2
 7:    7     2
 8:    8     2
 9:    9     2
10:   10     3
11:   11     3
12:   12     3
13:   13     4
14:   14     4
15:   15     4
16:   16     4
17:   17     4
18:   18     4
19:   19     4
20:   20     4

DT2
    obs group
 1:   1     1
 2:   2     1
 3:   3     1
 4:   4     2
 5:   5     2
 6:   6     2
 7:   7     3
 8:   8     3
 9:   9     3
10:  10     4
11:  11     4
12:  12     4
13:  13     5

因此,针对DT1中的每一行,我想通过分组将其与DT2中的2行随机配对。预期的结果可能类似于:

DT3
    var1 group obs
 1:    1     1   1
 2:    1     1   3
 3:    2     1   2
 4:    2     1   3
...
37:   19     4   10
38:   19     4   11
39:   20     4   10
40:   20     4   12

如有必要提醒一下,实际应用程序 DT1 中有 4 亿行数据,DT2 中有 1000 万行。
2个回答

3
您的需求的直接翻译是:
DT2[DT1, on=.(group), allow.cartesian=TRUE, .(var1, obs=obs[sample(.N, 2L)]), by=.EACHI]

这可能会更快:
gn <- DT1[, .(nsamp=2*.N), keyby=.(group)]
DT2[gn, on=.(group), .(obs=obs[sample(.N, nsamp, replace=TRUE)]), by=.EACHI][,
    var1 := rep(DT1$var1, each=2L)]

数据:

set.seed(0L)
library(data.table)
DT1 <- data.table(var1=101:120, group=c(1,1,1,1,1,2,2,2,2,3,3,3,4,4,4,4,4,4,4,4))
DT2 <- data.table(obs=201:213, group=c(1,1,1,2,2,2,3,3,3,4,4,4,5))

示例输出:

    group var1 obs
 1:     1  101 203
 2:     1  101 201
 3:     1  102 202
 4:     1  102 203
 5:     1  103 203
 6:     1  103 201
 7:     1  104 203
 8:     1  104 202
 9:     1  105 202
10:     1  105 203
11:     2  106 204
12:     2  106 206
13:     2  107 204
14:     2  107 205
15:     2  108 205
16:     2  108 206
17:     2  109 205
18:     2  109 206
19:     3  110 209
20:     3  110 207
21:     3  111 209
22:     3  111 208
23:     3  112 207
24:     3  112 208
25:     4  113 210
26:     4  113 212
27:     4  114 211
28:     4  114 210
29:     4  115 211
30:     4  115 212
31:     4  116 211
32:     4  116 210
33:     4  117 211
34:     4  117 210
35:     4  118 210
36:     4  118 211
37:     4  119 212
38:     4  119 211
39:     4  120 210
40:     4  120 211
    group var1 obs

一如既往,你做得很好。使用 allow.cartesian 的单行代码非常棒,谢谢! - bpar

2
这里有一种使用dplyr的方法,我相信这也可以转换为data.table。其逻辑是我们首先计算DT1中每个组的行数,然后通过group将它们与DT2进行left_join,并使用sample在每个group中随机选择n * 2行。"最初的回答"
library(dplyr)

DT1 %>%
   group_by(group) %>%
   mutate(n = n()) %>%
   left_join(DT2, by = "group") %>%
   group_by(group) %>%
   slice(sample(seq_len(n()), first(n) * 2, replace = TRUE)) %>%
   select(-n)

#    var1 group   obs
#   <int> <dbl> <int>
# 1     4     1     1
# 2     3     1     3
# 3     4     1     2
# 4     4     1     1
# 5     2     1     3
# 6     3     1     2
# 7     1     1     1
# 8     4     1     2
# 9     4     1     2
#10     3     1     2
# … with 30 more rows

感谢@Ronak Shah。使用dplyr的解决方案很好,但是从中并不清楚是否保留了DT1中的原始行?(这里的标识符将是列var1...)。似乎我们只是进行连接然后随机抽样,这可能不能保证DT1中的原始行在最终结果中得以保留? - bpar
@BryanMichael 如果你想在最终输出中包含 var1,那么可能需要使用 mutate。请参见更新的答案。这是你所问的吗? - Ronak Shah
还不太对...如果你加上 %>% arrange(var1),可能会更清楚问题所在。结果中每个 var1 应该只有2个。你肯定走在了正确的轨道上,我会试着调整一下,看看能否让它正常工作。一旦我找到解决方法,我会告诉你,并接受你的答案。谢谢! - bpar
我可能误解了问题。在我的尝试中,DT1 中没有行,所有行都来自 DT2,而且根本没有关于 var1 的条件。如果你所说的是真的,那么你期望的输出应该有60行,以便在结果中有每个 var1 的3个。 - Ronak Shah
你说得对,我在期望结果方面表述不太清楚,对此感到抱歉!我发布的期望结果只是来自DT2中的两个样本,即40行,而与DT1的最终联接实际上将是60行。话虽如此,@chinsoon12在下面的回答符合“data.table”的要求,正是我想要的。我认为你的“dplyr”解决方案也很接近。我会用它进一步完成并分享,以便其他人喜欢“dplyr”。 - bpar

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